实现刮刮乐和橡皮擦这类 Canvas 擦除效果,核心并不在“往上绘制什么”,而在“把上层内容真正擦除掉”。常见做法是借助 globalCompositeOperation 的 "destination-out" 模式,将顶层遮罩层直接抹去,而不是简单覆盖一层颜色。实际开发中,通常需要把绘制拆分为两层:底层放奖品或目标内容,顶层铺设一层不透明遮罩;当用户开始刮开或擦除时,再切换到对应的合成模式进行处理。同时,鼠标事件与触摸事件的坐标建议统一换算,避免在不同设备上出现位置偏移;再结合圆形擦除笔触与圆角线条细节,整体交互会更自然,滑动体验也会更顺畅。

使用 Canvas 制作刮刮乐效果或橡皮擦擦除功能,最关键的一步其实只有一个:将 globalCompositeOperation 设置为 "destination-out" 模式。这个模式并不是把新的图形覆盖到画布上,而是把当前已经存在的内容“擦掉”——更准确地说,它处理的是 alpha 透明通道,而不是直接修改颜色值。所以,这类效果的本质重点不在“怎么画上去”,而在“如何擦除掉”。
一、基础画布与遮罩层准备
Canvas 刮刮乐的本质逻辑是:底层先展示奖品内容(文字或图片),上层再覆盖一层不透明的遮罩色块(例如灰色、银色或黑色),用户手指或鼠标划过后,通过擦除顶层来露出下方内容。因此在实现时需要确保:
- 底层内容(奖品信息)先完成绘制,并且不能被后续擦除操作影响;
- 遮罩层需要单独绘制在顶层,通常使用纯色且不透明度为 1;
- 擦除操作只作用于遮罩层本身,不能误伤底层内容。
二、设置擦除模式的关键代码
在执行 Canvas 擦除之前,必须先正确切换合成模式,并同步设置好绘图样式:
- 调用
ctx.globalCompositeOperation = "destination-out"; - 设置
ctx.fillStyle或ctx.strokeStyle为任意不透明颜色(实际生效的是 alpha,颜色本身通常无关紧要); - 如果使用
fillRect或fill()来擦除,需确保路径闭合或擦除区域定义明确; - 擦除完成后,建议及时恢复默认合成模式:
ctx.globalCompositeOperation = "source-over",以免影响后续正常绘制。
三、支持鼠标和触摸的擦除逻辑
如果要让刮刮乐或橡皮擦效果同时兼容 PC 端和移动端,就需要同时监听 mousedown/mousemove/mouseup 与 touchstart/touchmove/touchend,并统一坐标计算方式:
- 获取事件坐标时,建议使用
getBoundingClientRect()计算相对于 canvas 画布的真实偏移; - 触摸事件通常读取
touches[0],鼠标事件则读取clientX/Y; - 推荐采用“画圆擦除”的方式处理轨迹(相较直接画线更自然):在移动过程中,以当前点为中心执行
fillCircle,半径一般可设为 20~40px; - 开启
ctx.lineCap = "round"和ctx.lineJoin = "round",可以让连续擦除轨迹更圆润、更平滑。
四、优化体验的小技巧
单纯的 Canvas 擦除效果有时会显得比较生硬,加入一些细节优化后,整体真实感和可用性会更好:
- 擦除时可以为遮罩层增加轻微模糊效果(例如配合
shadowBlur与同色shadowColor),模拟橡皮擦边缘的自然虚化; - 可以记录用户已擦除的面积占比,达到一定阈值(例如 60%)后,自动触发“全部刮开”的动画或提示反馈;
- 移动端要注意阻止默认行为:在
touchmove中调用e.preventDefault(),避免手指滑动时页面跟着滚动; - 如果场景对性能较敏感,可以把遮罩层单独绘制到离屏 canvas 中,主 canvas 只负责最终合成,从而降低重绘压力。
这类效果实现起来并不复杂,但有一个细节非常容易被忽略:Canvas 擦除不是“画一层白色”,而是“在上层遮罩中挖出透明区域”。只要合成模式设置正确、图层关系理清、事件坐标准确,HTML Canvas 刮刮乐和橡皮擦效果就能稳定实现。
