如何在JavaScript Canvas中恢复globalCompositeOperation=destination-out的遮罩区域?
Canvas遮罩恢复问题的解决方案
你现在的问题出在:用destination-out在交互画布上绘制时,是把画布对应区域的像素擦成透明(露出下方的原图),但切换到source-over时,你用橙色笔触去画,相当于在透明区域覆盖橙色,这当然不是你要的“恢复遮罩”效果——你要的是把之前挖开的洞补上,重新遮住原图。
完全不需要引入第三块Canvas,调整Konva的图层逻辑和合成模式就能解决:
核心实现思路
- 把遮罩操作单独放在交互画布的独立Layer里(Konva的Layer本质就是独立的Canvas),原图放在这个遮罩Layer的下方。
- 绘制遮罩(挖洞露原图):用
destination-out在遮罩Layer上绘制,相当于把遮罩层的对应像素擦除,露出下方原图。 - 擦除遮罩(补洞遮原图):切换到
source-over,用和遮罩Layer初始底色一致的不透明颜色(比如白色)绘制,把之前擦除的区域补回来,重新遮住原图。
调整后的核心代码
if (event.target.name() === "baseImage") { isDrawingMask.value = true; if (konvaStage.value) { currentLineMask.value = []; const pointerPosition = konvaStage.value.getNode().getPointerPosition(); const scale = konvaStage.value.getNode().scale(); const stagePosition = konvaStage.value.getNode().position(); if (pointerPosition && scale) { const calculatedX = (pointerPosition.x - stagePosition.x) / scale.x; const calculatedY = (pointerPosition.y - stagePosition.y) / scale.y; const isDrawMask = props.tool === "draw-mask"; currentLineMask.value = { stroke: isDrawMask ? "#df4b26" : "#ffffff", // 擦除时用遮罩层底色填充 strokeWidth: 60, globalCompositeOperation: isDrawMask ? "destination-out" : "source-over", lineCap: "round", lineJoin: "round", points: [calculatedX, calculatedY, calculatedX, calculatedY], }; } } }
关键注意点
- 确保遮罩Layer的初始状态是完全不透明(比如给Layer填充白色),这样一开始原图是被完全遮住的,
destination-out绘制时才会挖洞露出原图。 - 不要把遮罩操作和原图绘制放在同一个Layer,否则
destination-out会直接擦除原图像素,后续无法恢复。
内容的提问来源于stack exchange,提问作者muzak
相关产品推荐
相关产品推荐

