You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Canvas实现「绘制显词」效果遇阻:如何让白色文字被黑色笔触划过后方显现

Canvas实现「绘制显词」效果遇阻:如何让白色文字被黑色笔触划过后方显现

嘿,我完全get你要做的效果——白色背景上藏着白色文字,用黑色粗笔划过,文字就从黑色笔迹里“透”出来,对吧?你的代码现在的问题是层级逻辑搞反了,还没用到正确的合成模式控制文字显示范围,所以才会出现笔迹盖过文字的情况。

先给你理清楚问题出在哪:

  1. 你现在是用黑色画的文字,而不是需求里的白色;
  2. 绘制顺序搞反了:文字先画、笔迹后画,导致笔迹盖在文字上面,完全把文字挡住;
  3. 没有白色背景,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();

关键修改点说明

  1. 离屏Canvas存笔迹:把用户的笔迹和文字绘制完全分开,避免互相干扰,每次更新主Canvas都能精准控制显示内容;
  2. 合成模式source-atop:这是实现效果的核心——它让后续绘制的白色文字,只在已有黑色笔迹的区域显示,完美匹配你要的“划过哪里显哪里”;
  3. 修正事件逻辑:原来的isPainting状态搞反了,现在改成鼠标按下开始绘制、抬起停止,符合正常的绘画直觉;
  4. 白色背景+白色文字:平时文字和背景同色完全隐藏,只有在黑色笔迹区域才会形成对比显现出来。

如果要加多个文字,只需要在updateMainDisplay里多调用几次strokeText就行,调整lineWidth还能改变笔迹的粗细,非常灵活。

备注:内容来源于stack exchange,提问作者Hankkk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 09:40:30