Canvas实现「绘制显词」效果遇阻:如何让白色文字被黑色笔触划过后方显现
Canvas实现「绘制显词」效果遇阻:如何让白色文字被黑色笔触划过后方显现
嘿,我完全get你要做的效果——白色背景上藏着白色文字,用黑色粗笔划过,文字就从黑色笔迹里“透”出来,对吧?你的代码现在的问题是层级逻辑搞反了,还没用到正确的合成模式控制文字显示范围,所以才会出现笔迹盖过文字的情况。
先给你理清楚问题出在哪:
- 你现在是用黑色画的文字,而不是需求里的白色;
- 绘制顺序搞反了:文字先画、笔迹后画,导致笔迹盖在文字上面,完全把文字挡住;
- 没有白色背景,Canvas初始是透明的,也不符合你说的“白色背景”要求。
核心解决方案思路
我们要实现的是「白色文字只在黑色笔迹区域显现」,核心是用合成模式控制文字的显示范围:
- 白色背景 + 白色文字(平时和背景同色,自然隐藏)
- 黑色笔迹作为“显示载体”,通过
source-atop合成模式,让白色文字只出现在有黑色笔迹的地方
修改后的完整代码
<body> <section class="container"> <div class="drawing-board"> <canvas id="drawing-board"></canvas> </div> </section> <script src="./index.js"></script> </body>
const canvas = document.getElementById('drawing-board'); const ctx = canvas.getContext('2d'); const canvasOffsetX = canvas.offsetLeft; const canvasOffsetY = canvas.offsetTop; canvas.width = window.innerWidth - canvasOffsetX; canvas.height = window.innerHeight - canvasOffsetY; // 给Canvas设置白色背景(符合需求) ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); let isPainting = false; let lineWidth = 100; const brushColour = "black"; // 创建离屏Canvas专门存用户的笔迹,避免和文字绘制互相干扰 const offscreenCanvas = document.createElement('canvas'); const offscreenCtx = offscreenCanvas.getContext('2d'); offscreenCanvas.width = canvas.width; offscreenCanvas.height = canvas.height; offscreenCtx.fillStyle = 'transparent'; offscreenCtx.fillRect(0, 0, offscreenCanvas.width, offscreenCanvas.height); // 要隐藏的文字配置 const targetText = "StackOverFlow"; const textX = 100; const textY = 240; // 绘制笔迹的逻辑 const drawBrush = (e) => { if(!isPainting) return; offscreenCtx.lineWidth = lineWidth; offscreenCtx.lineCap = 'round'; offscreenCtx.strokeStyle = brushColour; offscreenCtx.lineTo(e.clientX - canvasOffsetX, e.clientY - canvasOffsetY); offscreenCtx.stroke(); // 每次画完笔迹,更新主Canvas的显示 updateMainDisplay(); }; // 更新主Canvas的显示逻辑(核心) const updateMainDisplay = () => { // 1. 清空主Canvas,重新填充白色背景 ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); // 2. 把离屏Canvas里的黑色笔迹画到主Canvas上 ctx.drawImage(offscreenCanvas, 0, 0); // 3. 关键:设置合成模式为source-atop // 接下来绘制的内容(白色文字)只会出现在已有像素(黑色笔迹)的区域上 ctx.globalCompositeOperation = 'source-atop'; // 4. 绘制白色文字(现在只会在黑色笔迹区域显示) ctx.strokeStyle = 'white'; ctx.font = "italic bold 15pt Tahoma"; ctx.strokeText(targetText, textX, textY); // 5. 重置合成模式,避免影响后续操作 ctx.globalCompositeOperation = 'source-over'; }; // 修正事件逻辑:鼠标按下开始画,抬起停止 canvas.addEventListener('mousedown', (e) => { isPainting = true; // 开始新的笔迹路径 offscreenCtx.beginPath(); offscreenCtx.moveTo(e.clientX - canvasOffsetX, e.clientY - canvasOffsetY); }); canvas.addEventListener('mouseup', () => { isPainting = false; }); canvas.addEventListener('mousemove', drawBrush); // 初始化主Canvas(无笔迹时文字隐藏) updateMainDisplay();
关键修改点说明
- 离屏Canvas存笔迹:把用户的笔迹和文字绘制完全分开,避免互相干扰,每次更新主Canvas都能精准控制显示内容;
- 合成模式
source-atop:这是实现效果的核心——它让后续绘制的白色文字,只在已有黑色笔迹的区域显示,完美匹配你要的“划过哪里显哪里”; - 修正事件逻辑:原来的
isPainting状态搞反了,现在改成鼠标按下开始绘制、抬起停止,符合正常的绘画直觉; - 白色背景+白色文字:平时文字和背景同色完全隐藏,只有在黑色笔迹区域才会形成对比显现出来。
如果要加多个文字,只需要在updateMainDisplay里多调用几次strokeText就行,调整lineWidth还能改变笔迹的粗细,非常灵活。
备注:内容来源于stack exchange,提问作者Hankkk
相关产品推荐
相关产品推荐

