直接用 ctx.globalCompositeOperation = 'destination-out' 能实现擦除,但必须先绘制覆盖层再设置该属性,否则擦除作用于空白画布而无反应;需用 boundingClientRect 获取真实尺寸并校准触摸坐标,fill 配合 arc 比 clearRect 更可控自然。

直接用 ctx.globalCompositeOperation = 'destination-out' 就能实现擦除效果,但必须在绘制覆盖层之后、监听事件之前设置,否则无效。
为什么 canvas 擦除总是没反应?
最常见的原因是 globalCompositeOperation 设置时机错误:它只对后续的绘制操作生效,如果在绘制完灰色/图片覆盖层之后才设置,那擦除动作就作用在空白画布上,自然看不到效果。
- 必须先调用
ctx.fillRect()或ctx.drawImage()绘制完整覆盖层 - 再执行
ctx.globalCompositeOperation = 'destination-out' - 最后绑定
touchstart/touchmove并在其中调用ctx.fill()或ctx.clearRect() - uni-app 的
uni.createCanvasContext()不支持直接读取像素,所以不能靠“判断透明度”来检测刮开面积,得靠记录路径或面积估算
touchmove 中该用 clearRect 还是 fill?
两者都行,但行为不同:clearRect 是硬擦除(不管当前合成模式),fill 依赖 globalCompositeOperation 才能擦出洞。推荐用 fill 配合 arc 或 rect 路径,更可控、边缘更自然。
-
clearRect(x - 20, y - 20, 40, 40)简单粗暴,适合方形刮擦,但多次重叠会留白边 -
ctx.beginPath(); ctx.arc(x, y, 20, 0, Math.PI * 2); ctx.fill()更贴近真实刮奖手感,且和destination-out协同工作稳定 - 注意:每次
fill()前不用ctx.save(),但要确保没漏掉ctx.beginPath(),否则路径会累加导致意外描边
uni-app 中 canvas 尺寸和坐标怎么对齐?
canvas 元素的 CSS 宽高 ≠ 实际绘图区域宽高,这是最常踩的坑。uni-app 必须用 boundingClientRect() 拿真实尺寸,并用 event.touches[0].pageX/Y 减去 rect.left/top 才能得到正确坐标。
- 别信
canvas.width和canvas.height属性——它们默认是 300×150,和样式无关 - 务必用
uni.createSelectorQuery().select('#myCanvas').boundingClientRect()获取渲染后的真实宽高 - 触摸坐标计算必须减去
rect.left和rect.top,不能直接用touch.x/touch.y(那是相对于视口的) - 在
touchmove里频繁调用ctx.draw(true)会卡顿,建议节流(如 16ms 一次)或累积多点再批量绘制
真正难的不是擦除,而是怎么可靠地判断“刮开了多少”。像素级检测在 uni-app 里走不通,getImageData 不可用;只能靠面积估算或路径记录——这点很容易被忽略,直到上线后中奖逻辑出 bug 才发现。


















