Canvas橡皮擦功能异常:拖拽画布全白,切换后恢复黑色求助
问题:Canvas橡皮擦功能导致所有笔触批量变色
我在Canvas绘图应用中实现了橡皮擦功能,用白色笔触模拟擦除效果,通过按钮切换绘制/擦除模式。应用带反向控制的十字准线,但点击橡皮擦按钮拖拽时,画布上所有笔触都会变成白色;再次点击按钮切换回绘制模式,所有笔触又变回黑色。
相关代码(main.js)
const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); let isDrawing = false; let lastX = 0; let lastY = 0; const AXIS_LENGTH = 50; // Length of the axis lines let mouseX; let mouseY; // history so we can always redraw var points = [] var allPoints = [] var invertedX, invertedY canvas.width = window.innerWidth; canvas.height = window.innerHeight; function startDrawing(e) { isDrawing = true; [lastX, lastY] = [canvas.width - (e.pageX - canvas.offsetLeft), canvas.height - (e.pageY - canvas.offsetTop)]; points = [] allPoints.push(points) points.push([lastX, lastY]) } function draw(e) { if (!isDrawing) return; const x = canvas.width - (e.pageX - canvas.offsetLeft); const y = canvas.height - (e.pageY - canvas.offsetTop); [lastX, lastY] = [x, y]; points.push([lastX, lastY]) } function stopDrawing() { isDrawing = false; } function clearCanvas() { ctx.clearRect(0, 0, canvas.width, canvas.height); } function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } function drawAllPoints() { allPoints.forEach(drawPoints) } function drawPoints(points) { if (points.length) { ctx.save() ctx.strokeStyle = 'black'; ctx.lineWidth = 5; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(points[0][0], points[0][1]); for (var i = 1; i < points.length; i++) { ctx.lineTo(points[i][0], points[i][1]); ctx.stroke(); } ctx.restore() } } function drawEverything() { clearCanvas() drawAxis(); drawAllPoints() } function drawAxis() { mouseX = invertedX mouseY = invertedY // Draw vertical axis line ctx.save() ctx.beginPath(); ctx.moveTo(mouseX, mouseY - AXIS_LENGTH / 2); ctx.lineTo(mouseX, mouseY + AXIS_LENGTH / 2); ctx.stroke(); // Draw horizontal axis line ctx.beginPath(); ctx.moveTo(mouseX - AXIS_LENGTH / 2, mouseY); ctx.lineTo(mouseX + AXIS_LENGTH / 2, mouseY); ctx.stroke(); ctx.restore() } canvas.addEventListener('mousedown', startDrawing); canvas.addEventListener('mousemove', draw); canvas.addEventListener('mouseup', stopDrawing); canvas.addEventListener('mouseout', stopDrawing); canvas.addEventListener('touchstart', (e) => { e.preventDefault(); startDrawing(e.touches[0]); }); canvas.addEventListener('touchmove', (e) => { e.preventDefault(); draw(e.touches[0]); }); canvas.addEventListener('touchend', stopDrawing); window.addEventListener('resize', resizeCanvas); document.addEventListener("mousemove", function(event) { const mouseX = event.clientX; const mouseY = event.clientY; invertedX = window.innerWidth - mouseX; invertedY = window.innerHeight - mouseY; drawEverything() });
问题根源与修复方案
核心问题
你的代码没有给每一笔绘制记录颜色信息,drawPoints函数固定用black作为绘制颜色。切换到橡皮擦模式时,所有历史笔触会被强制重绘成白色;切回绘制模式后又变回黑色,本质是每次重绘都用统一颜色覆盖了所有历史路径。
修复步骤
- 给每个笔触组添加颜色属性:记录每一笔绘制时的颜色(黑色/白色)
- 新增模式控制变量:用
isErasing标记当前是绘制还是擦除模式 - 修改重绘逻辑:根据每笔的颜色属性来设置绘制颜色
- 添加按钮切换逻辑:绑定按钮事件切换模式
修改后的完整代码
const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); const eraseBtn = document.getElementById('eraseBtn'); // 假设按钮id为eraseBtn let isDrawing = false; let isErasing = false; // 控制当前模式 let lastX = 0; let lastY = 0; const AXIS_LENGTH = 50; let mouseX; let mouseY; // 存储每个笔触的路径和颜色 var allPoints = [] var invertedX, invertedY canvas.width = window.innerWidth; canvas.height = window.innerHeight; // 按钮切换逻辑 eraseBtn.addEventListener('click', () => { isErasing = !isErasing; eraseBtn.textContent = isErasing ? '切换到绘制' : '切换到擦除'; }); function startDrawing(e) { isDrawing = true; const x = canvas.width - (e.pageX - canvas.offsetLeft); const y = canvas.height - (e.pageY - canvas.offsetTop); // 每个笔触组存储路径和对应颜色 const strokeGroup = { points: [[x, y]], color: isErasing ? 'white' : 'black' }; allPoints.push(strokeGroup); } function draw(e) { if (!isDrawing) return; const x = canvas.width - (e.pageX - canvas.offsetLeft); const y = canvas.height - (e.pageY - canvas.offsetTop); // 获取当前正在绘制的笔触组,追加路径点 const currentStroke = allPoints[allPoints.length - 1]; currentStroke.points.push([x, y]); } function stopDrawing() { isDrawing = false; } function clearCanvas() { ctx.clearRect(0, 0, canvas.width, canvas.height); } function resizeCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } function drawAllPoints() { allPoints.forEach(drawStrokeGroup) } function drawStrokeGroup(strokeGroup) { const { points, color } = strokeGroup; if (points.length) { ctx.save() ctx.strokeStyle = color; // 使用笔触组自身的颜色 ctx.lineWidth = 5; ctx.lineCap = 'round'; ctx.beginPath(); ctx.moveTo(points[0][0], points[0][1]); for (var i = 1; i < points.length; i++) { ctx.lineTo(points[i][0], points[i][1]); ctx.stroke(); } ctx.restore() } } function drawEverything() { clearCanvas() drawAxis(); drawAllPoints() } function drawAxis() { mouseX = invertedX mouseY = invertedY ctx.save() ctx.beginPath(); ctx.moveTo(mouseX, mouseY - AXIS_LENGTH / 2); ctx.lineTo(mouseX, mouseY + AXIS_LENGTH / 2); ctx.stroke(); ctx.beginPath(); ctx.moveTo(mouseX - AXIS_LENGTH / 2, mouseY); ctx.lineTo(mouseX + AXIS_LENGTH / 2, mouseY); ctx.stroke(); ctx.restore() } canvas.addEventListener('mousedown', startDrawing); canvas.addEventListener('mousemove', draw); canvas.addEventListener('mouseup', stopDrawing); canvas.addEventListener('mouseout', stopDrawing); canvas.addEventListener('touchstart', (e) => { e.preventDefault(); startDrawing(e.touches[0]); }); canvas.addEventListener('touchmove', (e) => { e.preventDefault(); draw(e.touches[0]); }); canvas.addEventListener('touchend', stopDrawing); window.addEventListener('resize', resizeCanvas); document.addEventListener("mousemove", function(event) { const mouseX = event.clientX; const mouseY = event.clientY; invertedX = window.innerWidth - mouseX; invertedY = window.innerHeight - mouseY; drawEverything() });
关键说明
- 每个笔触组现在是包含
points路径数组和color颜色的对象,确保重绘时用正确颜色渲染每一笔 isErasing变量控制当前模式,点击按钮时同步更新按钮文本drawStrokeGroup函数读取每个笔触组的颜色属性,彻底解决了所有笔触统一变色的问题
内容的提问来源于stack exchange,提问作者hirithik
相关产品推荐
相关产品推荐

