用 globalCompositeOperation = "destination-out" 可真删像素实现擦除:先绘原图,再以透明色描边/填充路径“挖掉”对应区域;需及时切回 "source-over",注意跨域、移动端事件及性能优化。

用 canvas 的 globalCompositeOperation = "destination-out" 实现擦除
这不是“隐藏图层”或“CSS 遮罩”,而是真正在画布上把像素删掉。关键在合成模式:destination-out 表示只保留源图形(即你画的擦除路径)**不重叠**的部分,相当于“挖掉”目标图像对应区域。
实操要点:
- 必须将原图先用
drawImage()绘制到canvas上,擦除操作只对 canvas 像素生效,不能直接作用于元素 - 擦除笔触本质是用透明色(或任意色)+
destination-out模式描边/填充路径,不是改 alpha 值 - 记得在开始绘制前调用
ctx.globalCompositeOperation = "destination-out",擦除完要切回"source-over",否则后续绘图会异常 - 移动端需监听
touchstart/touchmove,并用event.touches[0]取坐标,别只绑mouse事件
canvas 擦除后如何导出为图片
擦除只是修改了 canvas 的像素数据,不自动影响原始 。若需保存结果,调用 toDataURL() 即可,但要注意:
- 如果 canvas 跨域加载图片(比如从 CDN 读图),
toDataURL()会抛错SecurityError: Failed to execute 'toDataURL' on 'HTMLCanvasElement' - 解决办法:服务端设置
CORS头,或前端创建img时加crossOrigin="anonymous"属性(前提是服务端允许) - 导出格式默认是
image/png,若要 JPEG,传参toDataURL("image/jpeg", 0.9),第二个参数是质量(0–1)
性能差?试试分层 canvas 或限制擦除范围
全图高频擦除(尤其大图 + 移动端)容易掉帧。根本原因是每次擦除都在重绘整个 canvas 区域,且 destination-out 对 GPU 不友好。
立即学习“前端免费学习笔记(深入)”;
优化方向:
- 用两层 canvas:底层放原图(不可写),顶层 canvas 专门做擦除,最后合成输出 —— 减少重复绘制原图
- 擦除路径用
lineWidth控制粗细,但别设过大(如 >100),否则单次描边计算量激增 - 对超大图(>2000px),可先缩放显示,擦除坐标按比例映射回原图尺寸,导出时再用高分辨率 canvas 渲染一次
- 避免在
requestAnimationFrame中反复调用clearRect()或重置合成模式,这些操作本身有开销
为什么用 clip() 做擦除效果不推荐
有人尝试先 ctx.beginPath() + ctx.arc() 定义擦除区域,再 ctx.clip(),最后 drawImage() 绘原图——这看似“擦除”,实则是遮罩显示,像素并未删除,且无法导出擦除后的图像(toDataURL() 输出的是完整原图)。
真正需要“擦除”语义(即像素级移除、可保存、可叠加多次操作)时,destination-out 是唯一可靠路径。其他方式要么是视觉欺骗,要么破坏后续交互逻辑。
擦除的本质是像素操作,不是样式切换;一旦漏掉 globalCompositeOperation 的切换时机,或忽略跨域限制,整个流程就会静默失败。










