uni-app中canvas需按平台差异配置:canvas-id须为纯字符串,用uni.createCanvasContext配对;坐标需除pixelRatio换算;保存图片须用平台专属API;清空画布后须重置线条样式;touch事件需正确调用beginPath/lineTo/stroke;App端需设disable-scroll为true。
直接能用,但必须按平台差异写法来,否则 h5 能画、小程序白屏、ios 断线是常态。
canvas-id 和 createCanvasContext 必须配对
uni-app 的 canvas 不是 DOM 元素,getContext('2d') 一定返回 null。必须用 uni.createCanvasContext,且参数必须严格匹配模板里的 canvas-id 属性值(不是 id,也不是 ref):
-
canvas-id只能是纯字符串,不能含空格、变量或表达式,例如canvas-id="sign"✅,:canvas-id="id"❌ - 创建 context 必须在组件就绪后:H5 和小程序推荐在
onReady,App 端建议加nextTick或setTimeout延迟 10ms 再调 - App 端(尤其 iOS)必须传
this上下文:uni.createCanvasContext('sign', this),漏掉会静默失败
touch 坐标必须换算 pixelRatio 才准
直接用 e.touches[0].x 画出来的签名,在真机上普遍偏右下,因为屏幕坐标没转成 canvas 坐标系。关键换算逻辑:
- 先取设备像素比:
const pixelRatio = uni.getSystemInfoSync().pixelRatio - 再除:
mouseX = e.touches[0].x / pixelRatio,mouseY = e.touches[0].y / pixelRatio - H5 还要额外处理缩放:用
querySelector('#sign').getBoundingClientRect()拿实际宽高,算出缩放系数再校正 - 不换算 → 签名漂移、断笔、起笔点错位,iOS 尤其明显
保存图片各端 API 完全不同
别指望 canvas.toDataURL 通用 —— 小程序和 App 都不认这个方法,必须走平台专属接口:
- 微信/支付宝小程序:
uni.canvasToTempFilePath({ canvasId: 'sign', success: (res) => {...} }),漏掉success回调会静默失败 - H5:
canvas.toDataURL('image/png')得到 base64,如需存本地,得再用uni.downloadFile转临时路径 - App(Android/iOS):
uni.canvasToTempFilePath是唯一可用方式,且要求 canvas 的width和height属性值必须为整数,且与style中设置一致,否则返回空路径 - 导出前务必手动触发重绘:
ctx.draw(true)(App 端)或ctx.stroke()(H5),否则生成空白图
清空画布后线条变粗、偏移的真正原因
这不是 bug,是 ctx 状态残留。每次清空后不重置样式,下次绘制就会沿用上次的 lineWidth、lineCap,甚至累积未闭合路径:
- 清空逻辑不能只靠
ctx.clearRect(0, 0, w, h),还要重置状态:ctx.lineWidth = 2、ctx.lineCap = 'round'、ctx.strokeStyle = '#000' - 每次
touchstart开头必须ctx.beginPath(),touchmove中只ctx.lineTo(),最后在touchend或定时器里统一ctx.stroke() - 避免高频
stroke():每帧最多一次,否则 iOS 卡顿+线条抖动 - 缓存点序列比实时绘图更稳:把坐标存进数组,重绘时批量画,横竖屏切换也不乱
最易被忽略的是:canvas 组件的 disable-scroll 在 App 端必须设为 true,小程序要用 catchtouchmove,否则手指一滑页面滚动,签名直接中断——这个细节在开发机上看不出来,真机一测就暴露。











