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

React中实现按住鼠标拖动修改二维Div矩阵颜色的技术问询

实现React矩阵单元格的拖拽着色功能

要实现按住鼠标左键移动时给经过的矩阵单元格着色,你需要结合几个鼠标事件完成,同时要遵循React状态驱动UI的理念,不要直接操作DOM样式。

需要用到的核心事件

  • onMouseDown:按下鼠标左键时,标记开始着色状态,并给当前单元格上色
  • onMouseMove:鼠标移动时,如果处于着色状态,给经过的单元格上色
  • onMouseUp/onMouseLeave:松开鼠标或鼠标移出画布时,结束着色状态

你的现有代码问题

  1. 直接修改DOM元素的style.backgroundColor,不符合React状态管理规范,应该通过更新state让React自动渲染颜色变化
  2. 只绑定了onClick,无法处理拖拽过程中的连续着色
  3. 渲染逻辑存在错误:const clr = x.color应该取y.clr(x是行数组,y才是单个单元格对象);className里的x.x重复,应该用单元格自身的坐标标识

修改后的完整代码

import { useState, useEffect, useCallback } from "react";

function Grid() {
  const [canvas, setCanvas] = useState([[]]);
  const [isDrawing, setIsDrawing] = useState(false);

  // 创建30x30的初始矩阵
  const createGrid = () => {
    const newGrid = [];
    for (let i = 0; i < 30; i++) {
      newGrid.push([]);
      for (let j = 0; j < 30; j++) {
        newGrid[i].push({ x: i, y: j, clr: "white" });
      }
    }
    setCanvas(newGrid);
  };

  useEffect(createGrid, []);

  // 更新单元格颜色的通用方法
  const updateCellColor = useCallback((x, y) => {
    setCanvas(prevGrid => {
      const newGrid = [...prevGrid];
      newGrid[x][y] = { ...newGrid[x][y], clr: "red" };
      return newGrid;
    });
  }, []);

  // 鼠标按下:启动着色模式并给当前单元格上色
  const handleMouseDown = useCallback((e) => {
    const cell = e.target.dataset;
    updateCellColor(Number(cell.x), Number(cell.y));
    setIsDrawing(true);
  }, [updateCellColor]);

  // 鼠标移动:如果处于着色模式,给经过的单元格上色
  const handleMouseMove = useCallback((e) => {
    if (!isDrawing) return;
    const cell = e.target.dataset;
    if (cell.x && cell.y) {
      updateCellColor(Number(cell.x), Number(cell.y));
    }
  }, [isDrawing, updateCellColor]);

  // 鼠标松开/移出画布:结束着色模式
  const handleMouseUp = useCallback(() => {
    setIsDrawing(false);
  }, []);

  const handleMouseLeave = useCallback(() => {
    setIsDrawing(false);
  }, []);

  return (
    <div 
      className="canvas"
      onMouseUp={handleMouseUp}
      onMouseLeave={handleMouseLeave}
      style={{ display: "flex", flexDirection: "column" }}
    >
      {canvas.map((row, rowIndex) => (
        <div key={rowIndex} className="row">
          {row.map((cell) => (
            <div
              key={`${cell.x}-${cell.y}`}
              className="cell"
              data-x={cell.x}
              data-y={cell.y}
              style={{ 
                backgroundColor: cell.clr,
                width: "20px",
                height: "20px",
                border: "1px solid #ccc"
              }}
              onMouseDown={handleMouseDown}
              onMouseMove={handleMouseMove}
            />
          ))}
        </div>
      ))}
    </div>
  );
}

export default Grid;

代码说明

  • isDrawing状态用于跟踪是否处于着色模式,避免误触
  • 使用useCallback包裹事件处理函数,避免组件重渲染时创建新函数实例,提升性能
  • 通过data-x/data-y属性存储单元格坐标,方便事件中获取位置信息
  • 所有颜色更新都通过修改state实现,完全遵循React状态驱动UI的原则
  • 在父容器上绑定onMouseUp和onMouseLeave,确保鼠标移出单个单元格时也能正确结束着色

内容的提问来源于stack exchange,提问作者Anurag-210

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 14:25:22