Konva.JS图层相减实现:如何用鼠标擦除/显示指定图层
解决方案:实现图层擦除/恢复功能
要实现左键擦除normalLayer显示底层、右键恢复normalLayer的效果,核心是利用图层合成模式和遮罩层的思路,调整图层结构与绘制逻辑:
关键修改思路
- 新增一个全屏白色的遮罩层
drawingLayer,作为normalLayer的显示控制层 - 设置
normalLayer的合成模式为source-in,使其仅显示与drawingLayer重叠的区域 - 在
drawingLayer上绘制:- 左键用
destination-out模式擦除白色区域,让normalLayer对应位置消失,露出底层的redFilterLayer - 右键用
source-over模式绘制白色区域,恢复normalLayer的显示
- 左键用
修改后的完整代码
function initializeKonva() { let container = document.querySelector('#MagicContainerCanvas'); if (!container) { console.error('Container #MagicContainerCanvas not found'); return; } let containerWidth = container.offsetWidth; let containerHeight = container.offsetHeight; let stage = new Konva.Stage({ container: container, width: containerWidth, height: containerHeight }); // 底层:红色滤镜图层 let redFilterLayer = new Konva.Layer(); stage.add(redFilterLayer); // 中间层:遮罩控制层,默认全屏白色,让normalLayer完全显示 let drawingLayer = new Konva.Layer(); stage.add(drawingLayer); // 添加全屏白色矩形作为初始遮罩 let maskRect = new Konva.Rect({ x: 0, y: 0, width: containerWidth, height: containerHeight, fill: 'white' }); drawingLayer.add(maskRect); // 顶层:正常显示图层,设置合成模式仅显示与drawingLayer重叠的区域 let normalLayer = new Konva.Layer(); normalLayer.globalCompositeOperation('source-in'); stage.add(normalLayer); // 加载图片到对应图层 loadImage(getLightSkin, redFilterLayer, true); loadImage(getLightMask, normalLayer, false); // 画笔显示层 let brushLayer = new Konva.Layer(); stage.add(brushLayer); let isDrawing = false; let brushSize = 20; let currentLine; // 画笔光标 let brush = new Konva.Circle({ x: -50, y: -50, radius: brushSize / 2, stroke: 'black', strokeWidth: 1, fill: 'rgba(0,0,0,0.2)' }); brushLayer.add(brush); stage.on('mousedown', function(e) { isDrawing = true; let pos = stage.getPointerPosition(); currentLine = new Konva.Line({ stroke: 'white', strokeWidth: brushSize, // 左键擦除遮罩,右键恢复遮罩 globalCompositeOperation: e.evt.button === 0 ? 'destination-out' : 'source-over', points: [pos.x, pos.y], tension: 0.5, lineCap: 'round', lineJoin: 'round' }); drawingLayer.add(currentLine); }); stage.on('mousemove', function(e) { let pos = stage.getPointerPosition(); brush.position({ x: pos.x, y: pos.y }); brushLayer.batchDraw(); if (isDrawing && currentLine) { let newPoints = currentLine.points().concat([pos.x, pos.y]); currentLine.points(newPoints); drawingLayer.batchDraw(); } }); stage.on('mouseup', function() { isDrawing = false; }); stage.on('contextmenu', function(e) { e.evt.preventDefault(); }); }
核心逻辑解释
source-in合成模式:normalLayer仅会在drawingLayer有像素的区域显示,完美实现遮罩效果destination-out模式:在drawingLayer上绘制时,会擦除目标区域的像素(即白色遮罩),让normalLayer对应位置消失source-over模式:在drawingLayer上绘制白色,恢复遮罩区域,让normalLayer重新显示
内容的提问来源于stack exchange,提问作者Nikita Nikitin
相关产品推荐
相关产品推荐

