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

HTML5 Canvas中ctx.strokeStyle及画笔属性不生效问题求助

React白板应用画笔样式不生效的解决方案

问题根源

  • Canvas宽高设置顺序错误:先配置上下文的strokeStyle、lineWidth等样式,再修改canvas.width和canvas.height。Canvas宽高变更时会自动清空画布,并重置所有上下文属性到默认值(比如strokeStyle变回黑色、lineWidth变回1),导致之前设置的样式被覆盖。
  • 用useState存储Canvas上下文不合理:useState用于存储触发组件重渲染的状态,而Canvas上下文是稳定的引用值,用useRef存储更合适,既能避免不必要的重渲染,又能保证获取到最新的上下文实例。
  • 坐标状态的异步更新问题:useState的更新是异步的,在draw函数中无法实时获取最新的prevX和prevY,会导致绘制偏移或断裂,改用useRef存储坐标可解决此问题。

修复步骤

  1. 调整Canvas宽高设置顺序:先设置canvas.width和canvas.height,再配置上下文样式属性。
  2. 用useRef替代useState存储Canvas上下文、prevX和prevY。
  3. 绘制时使用useRef中的实时坐标值,确保绘制连续性。

修改后的完整代码

import React, { useEffect, useRef, useState } from "react";

const WhiteBoard = () => {
  const canvasRef = useRef(null);
  const [isDrawing, setIsDrawing] = useState(false);
  // 用useRef存储上下文和坐标
  const ctxRef = useRef(null);
  const prevCoordsRef = useRef({ x: 0, y: 0 });

  const divRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");
    ctxRef.current = ctx;

    const canvasdiv = divRef.current;
    const canvasheight = canvasdiv.clientHeight;
    const canvaswidth = canvasdiv.clientWidth;

    // 先设置canvas宽高,再配置样式
    canvas.width = canvaswidth;
    canvas.height = canvasheight;

    // 此时设置的样式不会被重置
    ctx.strokeStyle = "red";
    ctx.lineWidth = 2;
    ctx.lineCap = "round"; // 可选:让线条端点更圆润
    console.log("Stroke style set to:", ctx.strokeStyle);
  }, []);

  const startDrawing = (e) => {
    setIsDrawing(true);
    // 更新ref中的起始坐标
    prevCoordsRef.current = {
      x: e.nativeEvent.offsetX,
      y: e.nativeEvent.offsetY
    };
  };

  const draw = (e) => {
    if (!isDrawing || !ctxRef.current) return;

    const ctx = ctxRef.current;
    const { x: prevX, y: prevY } = prevCoordsRef.current;
    const currentX = e.nativeEvent.offsetX;
    const currentY = e.nativeEvent.offsetY;

    ctx.beginPath();
    ctx.moveTo(prevX, prevY);
    ctx.lineTo(currentX, currentY);
    ctx.stroke();

    // 更新最新坐标到ref
    prevCoordsRef.current = { x: currentX, y: currentY };
  };

  const stopDrawing = () => {
    setIsDrawing(false);
  };

  return (
    <div ref={divRef} className="w-100" style={{ height: "80vh" }}>
      <canvas
        ref={canvasRef}
        className="w-100 h-100"
        onMouseDown={startDrawing}
        onMouseMove={draw}
        onMouseUp={stopDrawing}
        onMouseOut={stopDrawing}
        style={{ border: "1px solid black" }}
      ></canvas>
    </div>
  );
};

export default WhiteBoard;

额外说明

  • 添加lineCap: "round"可让线条端点和连接处更圆润,提升白板绘制体验。
  • 给外层div设置固定高度(如height: "80vh"),确保Canvas能获取正确的容器高度,避免绘制区域偏移。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 22:33:24