用canvas实现图片动态遮罩最可控,需将原图与遮罩图形同绘于canvas,利用globalCompositeOperation='destination-in'或'destination-out'实现真遮罩,clip()适合固定路径但不支持渐变,导出时须匹配设备像素比确保清晰度。

用 canvas 实现图片动态遮罩最可控
纯 CSS 无法对图片做逐像素级的动态遮罩(比如渐变透明、波纹扩散、路径擦除),必须借助 canvas。核心思路是:把原图绘制到 canvas 上,再用另一层图形(如圆形、矩形、SVG 路径或噪声纹理)作为遮罩,通过 globalCompositeOperation = 'destination-in' 只保留重叠区域。
常见错误是直接在 img 上叠加 CSS 动画遮罩层——那只是视觉叠层,并非真正“遮罩图片内容”,导出或截图时遮罩不生效。
- 遮罩图形必须和图片在同一
canvas上绘制,不能靠 DOM 层叠 - 动画需用
requestAnimationFrame驱动,避免用setTimeout导致掉帧 - 若图片跨域,需设
img.crossOrigin = 'anonymous',否则canvas读取会报SecurityError
globalCompositeOperation 的关键取值怎么选
遮罩效果成败取决于这个属性。常用且安全的组合只有两个:
-
'destination-in':只显示新绘图形与已有内容重叠的部分(推荐用于“显形”类遮罩,如圆形放大露出图片) -
'destination-out':只显示已有内容中未被新图形覆盖的部分(适合“擦除”类效果,如从中心向外清空)
别用 'source-atop' 或 'xor' —— 它们行为不稳定,尤其在高 DPI 屏幕或缩放页面下易出错。示例片段:
立即学习“前端免费学习笔记(深入)”;
ctx.drawImage(img, 0, 0); ctx.globalCompositeOperation = 'destination-in'; ctx.beginPath(); ctx.arc(centerX, centerY, radius, 0, Math.PI * 2); ctx.fill(); // 此时 canvas 上只剩圆形区域内的图片
用 clip() 做路径遮罩更轻量但限制多
如果遮罩形状固定(如椭圆、多边形、SVG 路径),clip() 比复合操作更快,且不依赖 globalCompositeOperation 切换。
- 必须在
drawImage()前调用ctx.clip(),否则无效 - 动画需重绘整个路径(如改变
radius),不能靠 CSS transform 缩放 clip 区域 - 不支持渐变遮罩(如半透明边缘),因为
clip()是硬裁切
示例:
ctx.save(); ctx.beginPath(); ctx.ellipse(x, y, rx, ry, 0, 0, Math.PI * 2); ctx.clip(); ctx.drawImage(img, 0, 0); ctx.restore();
导出带遮罩的图片时要注意 canvas 尺寸
遮罩动画常伴随缩放或位移,但 canvas.toDataURL() 只导出画布原始尺寸内容。若你用 canvas.width/height 设为 300×200,但实际绘制区域是 600×400(靠 ctx.scale(2) 放大),导出图会模糊或截断。
- 导出前务必确保
canvas.width和canvas.height与目标分辨率一致 - 避免用 CSS 设置
canvas宽高(如style="width:300px"),这只会拉伸像素,不改变绘图坐标系 - 若需高清导出,按设备像素比缩放 canvas 内部尺寸:
canvas.width = width * window.devicePixelRatio
遮罩本身不复杂,难的是动画节奏、响应式适配和跨设备一致性——这些往往比实现遮罩逻辑花更多调试时间。










