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

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作为绘制颜色。切换到橡皮擦模式时,所有历史笔触会被强制重绘成白色;切回绘制模式后又变回黑色,本质是每次重绘都用统一颜色覆盖了所有历史路径。

修复步骤

  1. 给每个笔触组添加颜色属性:记录每一笔绘制时的颜色(黑色/白色)
  2. 新增模式控制变量:用isErasing标记当前是绘制还是擦除模式
  3. 修改重绘逻辑:根据每笔的颜色属性来设置绘制颜色
  4. 添加按钮切换逻辑:绑定按钮事件切换模式

修改后的完整代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:15:08