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

React中HTML Canvas清除后仍持续渲染线条的问题求助

问题:React Canvas绘图应用线条持续渲染异常,启用RoughJS后恢复正常

我正在尝试使用React结合HTML Canvas元素开发一款绘图应用,运行代码时点击并拖拽鼠标会出现线条持续渲染的异常效果;但移除标注为[EN ROUGH]的注释启用RoughJS绘制后,线条显示完全正常,无法理解该问题的成因,希望得到解答。

相关代码

import React, { useEffect, useLayoutEffect, useRef, useState } from "react";
///*[EN ROUGH]*/import rough from 'roughjs';

///*[EN ROUGH]*/const generator = rough.generator();

const createElement = (x1, y1, x2, y2, type) => {
  return { x1, y1, x2, y2, type };
};

const drawElement = (context, element) => {
  if (element.type === "connect") {
     context.moveTo(element.x1, element.y1);
     context.lineTo(element.x2, element.y2);
     context.stroke();
    ///*[EN ROUGH]*/const line = generator.line(element.x1, element.y1, element.x2, element.y2);
    ///*[EN ROUGH]*/context.draw(line);

  }
};

function App() {
  const [elements, setElements] = useState([]);
  const [drawing, setDrawing] = useState(false);

  useLayoutEffect(() => {
    const canvas = document.getElementById("canvas");
    const context = canvas.getContext("2d");

    context.clearRect(0, 0, canvas.width, canvas.height);
    ///*[EN ROUGH]*/const context = rough.canvas(canvas);

    //redraw all the components on re-render
    elements.forEach((element) => {
      drawElement(context, element);
    });
  });

  const handleMouseDown = (event) => {
    setDrawing(true);
    const { clientX, clientY } = event;

    const element = createElement(
      clientX,
      clientY,
      clientX,
      clientY,
      "connect"
    );
    setElements((prevState) => [...prevState, element]);
  };

  const handleMouseMove = (event) => {
    if (!drawing) return;
    const { clientX, clientY } = event;
    //index of the last element that was selected
    const lastElemIndex = elements.length - 1;
    const { x1, y1 } = elements[lastElemIndex];
    const updatedElement = createElement(x1, y1, clientX, clientY, "connect");

    //overwrite the previous element with the new x2, y2 coordinates of mouse move
    const elementsCopy = [...elements];
    elementsCopy[lastElemIndex] = updatedElement;
    setElements(elementsCopy);
  };

  const handleMouseUp = () => {
    setDrawing(false);
  };

  return (
    <canvas
      id="canvas"
      width={window.innerWidth}
      height={window.innerHeight}
      onMouseDown={handleMouseDown}
      onMouseUp={handleMouseUp}
      onMouseMove={handleMouseMove}
    >
      Canvas
    </canvas>
  );
};

export default App;

问题成因分析

  • 原生Canvas路径未重置:Canvas 2D上下文的路径状态是累积的。在drawElement函数中,每次绘制线条时直接调用moveTo、lineTo和stroke,但没有调用beginPath()来开启新路径。这导致每次重绘时,之前的路径会被保留并再次执行stroke,叠加后就出现了线条持续渲染的异常效果。
  • RoughJS自动管理路径:RoughJS的draw方法内部会自动处理路径的创建与清理,每次绘制都会生成独立的路径并完成绘制,不会保留上下文的路径状态,因此不会出现叠加渲染的问题。

解决方案

1. 重置Canvas路径

修改drawElement函数,在绘制前调用beginPath(),确保每次绘制都是独立的新路径:

const drawElement = (context, element) => {
  if (element.type === "connect") {
     context.beginPath(); // 新增:开启新路径,避免路径累积
     context.moveTo(element.x1, element.y1);
     context.lineTo(element.x2, element.y2);
     context.stroke();
  }
};

2. 优化useLayoutEffect的依赖

为useLayoutEffect添加依赖数组,只在elements变化时触发重绘,避免不必要的性能消耗:

useLayoutEffect(() => {
  const canvas = document.getElementById("canvas");
  const context = canvas.getContext("2d");

  context.clearRect(0, 0, canvas.width, canvas.height);

  elements.forEach((element) => {
    drawElement(context, element);
  });
}, [elements]); // 仅当elements变化时执行重绘逻辑

内容的提问来源于stack exchange,提问作者Tushar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 22:52:48