
本文介绍在 Konva/React 画布中,利用 Canvas 2D 的 destination-out 合成模式高效实现支持图层顺序的“战争迷雾”效果——后绘制的遮罩区域(如黑色隐藏区)可覆盖先绘制的透明区域,避免逐像素遍历带来的性能瓶颈。
本文介绍在 konva/react 画布中,利用 canvas 2d 的 `destination-out` 合成模式高效实现支持图层顺序的“战争迷雾”效果——后绘制的遮罩区域(如黑色隐藏区)可覆盖先绘制的透明区域,避免逐像素遍历带来的性能瓶颈。
在虚拟桌面(VTT)类应用中,“战争迷雾”需满足两个关键要求:
- 层级优先级:后添加的遮罩形状(如 HIDE 类型)应覆盖先添加的 REVEAL 区域;
- 高性能渲染:避免对每个像素做 O(N×W×H) 的暴力遍历(如原方案中每帧生成上万 Rect 组件),尤其在高分辨率画布(如 2000×1500)下必须规避。
✅ 正确解法:Canvas 2D 合成模式 destination-out
globalCompositeOperation = 'destination-out' 是核心突破口:
- 它表示「仅保留目标图像中 未被源图像覆盖 的部分」;
- 换言之:用新绘制的形状(源)“擦除”已有内容(目标),且严格遵循绘制顺序——后绘者决定最终可见性。
实现步骤(Konva 中集成)
Konva 支持自定义 drawFunc,我们可直接操作底层 Canvas Context:
import { Layer, Rect } from 'konva';
// 创建 Fog-of-War 专用 Layer
const FowLayer = () => {
const fowShapes = useRecoilValue(fowShapesState); // 假设从状态管理获取
return (
<layer listening="{false}" drawfunc="{(ctx)"> {
const canvas = ctx.canvas;
const stage = ctx.getStage();
const width = stage?.width() || 1024;
const height = stage?.height() || 768;
// 1. 先绘制全黑背景(初始完全隐藏)
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, width, height);
// 2. 设置合成模式:后续绘制将“擦除”黑色区域
ctx.globalCompositeOperation = 'destination-out';
// 3. 按渲染顺序(即数组顺序)绘制所有 REVEAL 区域
// 注意:HIDE 区域不绘制!只用 REVEAL 来“挖空”黑幕
fowShapes.forEach((shape) => {
if (shape.type === TOOL_ENUM.REVEAL) {
ctx.fillStyle = 'white'; // 白色作为“擦除笔”
ctx.fillRect(shape.x, shape.y, shape.width, shape.height);
}
});
// 4. 重置合成模式(避免影响其他图层)
ctx.globalCompositeOperation = 'source-over';
}}
/>
);
};</layer>
? 关键逻辑说明:
- 初始全黑 → 所有区域默认隐藏;
- destination-out + 白色矩形 → 在黑底上“擦出”透明通道(即露出下方地图层);
- 绘制顺序天然保证优先级:后出现的 REVEAL 形状会覆盖(擦除)前面已擦出的区域,若需“局部隐藏”,只需在其上方叠加一个 HIDE 类型的 黑色覆盖层(见进阶优化)。
⚠️ 注意事项与进阶优化
-
不要混合使用 HIDE 和 REVEAL 在同一合成流程中:destination-out 仅适用于“擦除”操作。若需动态切换 HIDE(补黑)和 REVEAL(擦白),推荐双层策略:
// Layer 1: 黑底 + 所有 REVEAL(destination-out) // Layer 2: 透明底 + 所有 HIDE(source-over,直接覆盖为黑)
-
性能对比:原方案在 1000×1000 画布中需渲染 1M 个
,而本方案仅调用数次 fillRect(),GPU 加速下帧率稳定 60fps; - 抗锯齿与边缘处理:对圆形/多边形 REVEAL,使用 ctx.beginPath() + ctx.fill() 并开启 ctx.imageSmoothingEnabled = false 避免模糊;
- Konva 兼容性:确保 drawFunc 中调用的是原生 Canvas 2D API(非 Konva 封装),否则合成模式可能失效。
总结
destination-out 不是“XOR”的替代品,而是语义更精准的“遮罩擦除”工具。它天然尊重绘制时序、零像素计算开销、完美契合“后生效”需求。抛弃逐像素布尔数组,拥抱 Canvas 合成模式——这是 VTT 类应用实现高性能战争迷雾的工业级实践。











