uni.createCanvasContext必须传canvas-id字符串且不可省略this参数,touch坐标需除以pixelRatio校准,导出前须调ctx.draw(),清空画布应重置点数组并重绘。

uni.createCanvasContext 必须传 canvas-id,不能用 ref 或 id
在小程序(微信/支付宝)里直接 document.getElementById 或 this.$refs.canvas.getContext('2d') 会返回 null,因为 uni-app 的 canvas 是原生组件封装,没挂载到 DOM 树上。真机和模拟器都一样失效。
必须用 uni.createCanvasContext,且第一个参数是模板中写的 canvas-id 字符串值,比如:
<canvas canvas-id="signCanvas"></canvas>
对应调用:uni.createCanvasContext('signCanvas', this) —— 第二个参数 this 表示当前页面实例,不可省略,否则 H5 和小程序行为不一致。
-
canvas-id必须是静态字符串,不能是变量、不能含空格或特殊符号 - 别在
onLoad就调,要等组件渲染完成,推荐放在onReady或nextTick后执行 - H5 端若用了
type="2d",需改用uni.createSelectorQuery获取节点再创建 context,但签名场景建议统一用默认模式 +canvas-id,避免分支逻辑
touch 坐标必须除以 pixelRatio 才准,否则偏移几十像素
小程序的 e.touches[0].x 是屏幕物理坐标,而 canvas 内部坐标系受设备像素比影响。直接画上去,iOS 上常偏右下,安卓可能断笔、点不准。
正确做法是:拿到 touch 坐标后,先除以 uni.getSystemInfoSync().pixelRatio,再传给 moveTo / lineTo。
- 如果
canvas组件设了width/height(比如width="750rpx"),实际渲染尺寸可能被拉伸,此时还要结合getBoundingClientRect算缩放系数(仅 H5 需要) - App 端建议固定宽高(如
width="300px" height="200px"),不用百分比或 rpx,减少缩放干扰 - 加距离阈值过滤高频点:
if (distance > 2) { points.push(...) },防低端机卡顿
导出图片必须先 drawCanvas,canvasToTempFilePath 才不返回空图
canvasToTempFilePath 不会自动触发绘制,它只读取 canvas 当前帧缓存。如果没显式调用绘制,返回路径就是空白图,且无报错、静默失败。
导出前务必先调一次 ctx.draw()(注意不是 draw(true)),确保所有点已落笔渲染:
this.ctx.draw(false, () => {
uni.canvasToTempFilePath({
canvasId: 'signCanvas',
success: res => console.log(res.tempFilePath),
fail: err => console.error(err)
}, this)
})
-
canvasId必须和模板中完全一致,大小写、引号都不能错 -
success回调不能漏,失败时不会抛异常,只返回空字符串 - H5 下需确保域名在白名单、协议为 HTTPS,否则
canvasToTempFilePath直接拒绝执行 - 别用
toDataURL,它在小程序里不可用,H5 虽支持但跨域限制多,统一走canvasToTempFilePath
清空画布不能 clearRect,得重置点数组 + 重绘
很多初学者写 ctx.clearRect(0, 0, width, height),但在小程序 canvas 上基本无效 —— 它清的是当前帧缓存,但下次 draw() 仍会把缓存点重画出来。
签名本质是“存点 + 批量重绘”,清空逻辑应该是:
- 清空点数据结构(比如
this.points = []或this.strokes = []) - 立刻调一次
ctx.draw()触发空白重绘 - 如果用了背景色,记得重设:
ctx.setFillStyle('#fff'); ctx.fillRect(0, 0, width, height)
否则用户点“清除”后看似白了,一动手指又冒出旧轨迹——那是点数组没清,重绘时又画了一遍。











