canvas合成是alpha驱动的像素开关而非颜色混合,依据新旧像素alpha值按布尔规则决定显隐,如source-over为覆盖、destination-in为交集、source-out为挖空填入,clearrect因source alpha为0不参与多数合成。

理解这一点,就能避开“为什么设了 multiply 却没变暗”“为什么 destination-out 擦不掉”这类常见困惑——问题往往出在误把合成当成滤镜,而忽略了它底层是基于像素存在与否的位运算式决策。
合成的本质:Alpha 通道驱动的像素开关
Canvas 合成操作作用于每个像素的 RGBA 四元组。关键不是颜色值本身,而是该像素是否“有内容”(即 Alpha > 0)。所有合成模式都围绕两个条件判断:
• 旧画布上该位置是否有非透明像素(destination alpha > 0)
• 新绘制图形在该位置是否有非透明像素(source alpha > 0)
- source-over:只要 source 有像素就显示它;否则显示 destination —— 这就是“覆盖”的直觉来源
- destination-in:仅当 destination 和 source 都有像素时才保留 destination 像素,其余清空 → 实质是“取交集”
- source-out:仅当 destination 完全透明(alpha = 0)且 source 有像素时才绘制 source → “挖空后填入”
- lighter:对每个通道做数值相加(clamped to 255),属于少数真正做算术的模式,但仍是逐通道整数加,非浮点混合
为什么 clearRect 不参与大多数合成?
clearRect(x, y, w, h) 实际执行的是“绘制一个全透明黑色矩形”。它的 source alpha 全为 0,因此:
- 对
source-in、source-out等依赖 source alpha 的模式,它什么也不画 - 对
destination-out,它无法擦除任何内容,因为 destination-out 要求 source 有像素才能擦 destination —— 而 clearRect 的 source 没有像素 - 真正擦除要用
ctx.globalCompositeOperation = 'destination-out'+ 实际绘制(如 fillRect 或 drawImage)
drawImage 也受合成控制
图像绘制不是特例,它和 fillRect、arc.fill() 一样,是“新图形”输入。这意味着:
- 用
source-in+ 圆形路径 fill() + drawImage,可实现圆形裁剪 - 用
destination-in+ 文字路径 fill() + drawImage,可让图片只保留在文字形状内 - PNG 图像的 Alpha 通道会完整参与判断 —— 半透明边缘也会被当作“有像素”处理
调试时容易踩的坑
合成效果异常,大概率不是代码写错,而是状态没清理或前提不满足:
- 没调用
fill()或stroke():路径定义完必须显式填充/描边,否则不生成像素,合成无对象 - 启用了
shadowBlur:阴影像素也被视为“新图形”,会意外参与合成,建议调试期关闭 - 画布初始为空(全透明):此时
destination-over会把新图形直接画在页面背景上,而非“底下” - 跨域图片未设置
crossOrigin="anonymous":getImageData 报错,但合成本身不受影响;不过若后续要做像素处理,就会中断流程











