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

React Canvas绘制新线条时原有线条消失问题求助

解决React中Hangman游戏Canvas绘图丢失的问题

问题根源

  1. Canvas宽高重置清空画布:每次useEffect触发时,设置canvas.width和height会直接清空整个画布,这是Canvas的原生特性。
  2. 分步绘制逻辑缺陷:当前draw函数仅根据当前guessesLeft绘制单一步骤的线条,没有重绘之前所有已完成的部件,导致旧线条丢失。

修复步骤

1. 修改draw函数,重绘所有已解锁的部件

把原来的switch改成条件判断,让函数绘制从初始状态到当前guessesLeft对应的所有线条:

const draw = (ctx) => {
  ctx.lineWidth = 3;
  ctx.strokeStyle = "#F2F2F0";

  // 绘制底座(剩余猜测数≤9时显示)
  if (guessesLeft <= 9) {
    ctx.beginPath();
    ctx.moveTo(ctx.canvas.width / 2 - 250, ctx.canvas.height - 50);
    ctx.lineTo(ctx.canvas.width / 2 + 250, ctx.canvas.height - 50);
    ctx.stroke();
  }

  // 绘制竖杆(剩余猜测数≤8时显示)
  if (guessesLeft <= 8) {
    ctx.beginPath();
    ctx.moveTo(ctx.canvas.width / 2 - 200, ctx.canvas.height - 50);
    ctx.lineTo(ctx.canvas.width / 2 - 200, 50);
    ctx.stroke();
  }

  // 后续部件(横杆、绳子、头、身体等)都按此逻辑添加
};

2. 避免不必要的Canvas宽高重置(可选,适配响应式)

如果不需要动态调整画布大小,可把宽高设置移出useEffect,仅在组件挂载时初始化:

// Canvas.jsx
useEffect(() => {
  const canvas = canvasRef.current;
  const context = canvas.getContext("2d");

  // 只在组件首次挂载时设置宽高,避免每次重绘清空画布
  if (canvas.width === 0) {
    canvas.width = canvas.offsetWidth;
    canvas.height = canvas.offsetHeight;
  }

  draw(context);
}, [draw]);

如果需要响应式适配窗口大小,建议添加防抖的resize监听,调整宽高前先保存画布内容再恢复:

// Canvas.jsx 响应式版本示例
useEffect(() => {
  const canvas = canvasRef.current;
  const context = canvas.getContext("2d");

  const resizeCanvas = () => {
    // 保存当前画布内容
    const imageData = context.getImageData(0, 0, canvas.width, canvas.height);
    // 调整宽高
    canvas.width = canvas.offsetWidth;
    canvas.height = canvas.offsetHeight;
    // 恢复原有内容
    context.putImageData(imageData, 0, 0);
    // 重新绘制当前状态,适配新尺寸
    draw(context);
  };

  // 初始化设置
  resizeCanvas();
  // 监听窗口尺寸变化
  window.addEventListener('resize', resizeCanvas);

  return () => window.removeEventListener('resize', resizeCanvas);
}, [draw]);

3. 优化React渲染性能(可选)

用useCallback包裹draw函数,避免每次组件渲染时创建新的函数实例,减少Canvas组件的不必要重绘:

// App.jsx
const draw = useCallback((ctx) => {
  // 上述修改后的draw逻辑
}, [guessesLeft]);

为什么之前的尝试没用?

  • React.memo和useCallback仅能减少组件重绘次数,但核心问题是Canvas被清空且未重绘旧内容,因此无法解决线条丢失。
  • 调整beginPath和stroke的位置只能影响单条路径的绘制,无法改变"仅绘制当前步骤"的逻辑缺陷。

内容的提问来源于stack exchange,提问作者Jonas Bjørn Pedersen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 05:16:05