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

如何基于react-pdf Canvas实现工程图纸的动态鼠标绘图?

在react-pdf Canvas组件上实现鼠标交互绘图(直线/正方形)

要实现鼠标驱动的绘图功能,核心是通过监听鼠标事件管理绘图状态,再结合react-pdf的Canvas组件动态渲染图形。以下是具体实现方案:

1. 基础状态管理

首先用React状态记录绘图关键信息:

  • 绘图状态(是否正在绘制)
  • 起始点坐标
  • 当前鼠标位置
  • 已完成的图形列表
  • 当前选中的绘图工具(直线/正方形)
import { useState, useRef } from 'react';
import { Canvas } from '@react-pdf/renderer';

const PdfAnnotator = () => {
  const [isDrawing, setIsDrawing] = useState(false);
  const [startPoint, setStartPoint] = useState({ x: 0, y: 0 });
  const [currentPoint, setCurrentPoint] = useState({ x: 0, y: 0 });
  const [shapes, setShapes] = useState([]);
  const [tool, setTool] = useState('line'); // 可选值:'line' | 'rectangle'
  const canvasRef = useRef(null);

  // 后续事件处理和绘图逻辑
};

2. 监听鼠标事件

在Canvas外层包裹容器,监听鼠标按下、移动、松开事件,同时处理DOM坐标到PDF Canvas坐标的转换:

// 把鼠标的DOM坐标转为PDF Canvas的相对坐标
const getCanvasRelativeCoords = (e) => {
  const canvas = canvasRef.current;
  if (!canvas) return { x: 0, y: 0 };
  const rect = canvas.getBoundingClientRect();
  // 若PDF有缩放,需调整此处scale系数匹配缩放比例
  const scale = 1;
  return {
    x: (e.clientX - rect.left) * scale,
    y: (e.clientY - rect.top) * scale,
  };
};

// 鼠标按下:启动绘图流程
const handleMouseDown = (e) => {
  const coords = getCanvasRelativeCoords(e);
  setIsDrawing(true);
  setStartPoint(coords);
  setCurrentPoint(coords);
};

// 鼠标移动:更新实时绘图位置
const handleMouseMove = (e) => {
  if (!isDrawing) return;
  const coords = getCanvasRelativeCoords(e);
  setCurrentPoint(coords);
};

// 鼠标松开:完成当前图形并保存
const handleMouseUp = () => {
  if (!isDrawing) return;
  setShapes([
    ...shapes,
    {
      type: tool,
      start: startPoint,
      end: currentPoint,
      color: 'red',
    },
  ]);
  setIsDrawing(false);
};

3. 动态渲染图形

在Canvas的paint函数中,先绘制已保存的所有图形,再绘制当前正在绘制的临时图形:

const renderShapes = (painter) => {
  // 绘制已完成的图形
  shapes.forEach((shape) => {
    painter.save();
    painter.stroke(shape.color);
    painter.lineWidth(2);

    if (shape.type === 'line') {
      painter.moveTo(shape.start.x, shape.start.y);
      painter.lineTo(shape.end.x, shape.end.y);
    } else if (shape.type === 'rectangle') {
      const width = shape.end.x - shape.start.x;
      const height = shape.end.y - shape.start.y;
      painter.rect(shape.start.x, shape.start.y, width, height);
    }

    painter.stroke();
    painter.restore();
  });

  // 绘制临时的正在绘制的图形(虚线区分)
  if (isDrawing) {
    painter.save();
    painter.stroke('blue');
    painter.lineWidth(2);
    painter.setLineDash([5, 5]);

    if (tool === 'line') {
      painter.moveTo(startPoint.x, startPoint.y);
      painter.lineTo(currentPoint.x, currentPoint.y);
    } else if (tool === 'rectangle') {
      const width = currentPoint.x - startPoint.x;
      const height = currentPoint.y - startPoint.y;
      painter.rect(startPoint.x, startPoint.y, width, height);
    }

    painter.stroke();
    painter.restore();
  }
};

return (
  <div style={{ position: 'relative' }} onMouseDown={handleMouseDown} onMouseMove={handleMouseMove} onMouseUp={handleMouseUp}>
    <Canvas
      ref={canvasRef}
      style={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%' }}
      paint={(painter) => renderShapes(painter)}
    />
    {/* 可选:添加工具切换按钮 */}
    <div style={{ position: 'absolute', bottom: 10, left: 10 }}>
      <button onClick={() => setTool('line')} style={{ marginRight: 10 }}>直线</button>
      <button onClick={() => setTool('rectangle')}>正方形</button>
    </div>
  </div>
);

4. 测量功能扩展

如果需要计算图形实际尺寸,可利用保存的坐标点计算。例如计算直线长度:

// 计算两点间距离(基于PDF坐标,需结合PDF实际尺寸转换为物理单位)
const calculateDistance = (point1, point2) => {
  const dx = point2.x - point1.x;
  const dy = point2.y - point1.y;
  return Math.sqrt(dx * dx + dy * dy);
};

// 在渲染直线时添加长度标注示例
if (shape.type === 'line') {
  const midX = (shape.start.x + shape.end.x) / 2;
  const midY = (shape.start.y + shape.end.y) / 2;
  const distance = calculateDistance(shape.start, shape.end);
  painter.fillText(`${distance.toFixed(2)} pt`, midX, midY);
}

关键注意事项

  • 坐标转换:若PDF页面有缩放,需调整scale系数确保鼠标位置与Canvas坐标匹配。
  • 性能优化:图形数量较多时,可使用useMemo缓存绘制逻辑,避免不必要重绘。
  • 样式隔离:确保Canvas与外层容器的定位样式正确(如绝对定位),防止鼠标事件偏移。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 06:15:38