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

ReactJS中实现图片叠加鼠标追踪数据及跨窗口传图问题

解决方案

一、在原页面叠加鼠标追踪信息

直接用React的ref和事件监听解决图片位置获取问题,通过canvas叠加在图片上方绘制轨迹:

  1. 使用useRef获取图片和canvas元素,替代原生DOM查询
  2. 监听图片load事件确保加载完成,同时监听页面scroll事件实时更新图片位置
  3. 用相对定位容器包裹图片和canvas,让canvas绝对定位在图片上方
import { useRef, useEffect, useState } from 'react';

function SketchReplay({ imgSrc, mouseTracks, originalCanvasWidth, originalCanvasHeight }) {
  const imgRef = useRef(null);
  const canvasRef = useRef(null);
  const [imgScale, setImgScale] = useState({ x: 1, y: 1 });

  useEffect(() => {
    const updateCanvas = () => {
      if (!imgRef.current) return;
      // 获取图片实际显示尺寸
      const displayWidth = imgRef.current.offsetWidth;
      const displayHeight = imgRef.current.offsetHeight;
      // 计算缩放比例(原始画布尺寸 → 当前显示尺寸)
      setImgScale({
        x: displayWidth / originalCanvasWidth,
        y: displayHeight / originalCanvasHeight
      });
      drawTracks();
    };

    const drawTracks = () => {
      if (!canvasRef.current || !imgScale.x) return;
      const ctx = canvasRef.current.getContext('2d');
      const { width, height } = imgRef.current;
      canvasRef.current.width = width;
      canvasRef.current.height = height;
      ctx.clearRect(0, 0, width, height);

      // 绘制鼠标轨迹
      ctx.strokeStyle = '#ff0000';
      ctx.lineWidth = 2;
      ctx.beginPath();
      mouseTracks.forEach((point, idx) => {
        const x = point.x * imgScale.x;
        const y = point.y * imgScale.y;
        idx === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
      });
      ctx.stroke();
    };

    // 绑定事件
    imgRef.current?.addEventListener('load', updateCanvas);
    window.addEventListener('scroll', updateCanvas);
    window.addEventListener('resize', updateCanvas);
    updateCanvas();

    // 清理事件
    return () => {
      imgRef.current?.removeEventListener('load', updateCanvas);
      window.removeEventListener('scroll', updateCanvas);
      window.removeEventListener('resize', updateCanvas);
    };
  }, [mouseTracks, originalCanvasWidth, originalCanvasHeight]);

  return (
    <div style={{ position: 'relative', display: 'inline-block' }}>
      <img
        ref={imgRef}
        src={imgSrc}
        alt="Sketchpad screenshot"
        style={{ maxWidth: '100%', display: 'block' }}
      />
      <canvas
        ref={canvasRef}
        style={{
          position: 'absolute',
          top: 0,
          left: 0,
          pointerEvents: 'none' // 不遮挡图片点击事件
        }}
      />
    </div>
  );
}

二、在新窗口打开并叠加数据

通过window.open创建新窗口,直接在新窗口中动态生成图片和canvas,传递轨迹数据进行绘制:

方案1:直接在新窗口创建DOM

function SketchViewer({ imgSrc, mouseTracks, originalCanvasWidth, originalCanvasHeight }) {
  const openReplayWindow = () => {
    const newWindow = window.open('', '_blank', 'width=800,height=600');
    if (!newWindow) return;

    newWindow.onload = () => {
      // 创建容器
      const container = newWindow.document.createElement('div');
      container.style.position = 'relative';
      newWindow.document.body.style.margin = '0';
      newWindow.document.body.appendChild(container);

      // 创建图片
      const img = newWindow.document.createElement('img');
      img.src = imgSrc;
      img.style.maxWidth = '100%';
      img.style.display = 'block';
      container.appendChild(img);

      // 创建canvas
      const canvas = newWindow.document.createElement('canvas');
      canvas.style.position = 'absolute';
      canvas.style.top = '0';
      canvas.style.left = '0';
      canvas.style.pointerEvents = 'none';
      container.appendChild(canvas);

      // 图片加载完成后绘制轨迹
      img.onload = () => {
        const scaleX = img.width / originalCanvasWidth;
        const scaleY = img.height / originalCanvasHeight;
        canvas.width = img.width;
        canvas.height = img.height;
        const ctx = canvas.getContext('2d');
        ctx.strokeStyle = '#ff0000';
        ctx.lineWidth = 2;
        ctx.beginPath();
        mouseTracks.forEach((point, idx) => {
          const x = point.x * scaleX;
          const y = point.y * scaleY;
          idx === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
        });
        ctx.stroke();
      };
    };
  };

  return (
    <img
      src={imgSrc}
      alt="Sketchpad screenshot"
      onClick={openReplayWindow}
      style={{ cursor: 'pointer', maxWidth: '100%' }}
    />
  );
}

方案2:用localStorage传递大数据(适合图片是base64或大文件的情况)

// 主页面点击事件
const openReplayWindow = () => {
  // 临时存储数据
  localStorage.setItem('sketchImg', imgSrc);
  localStorage.setItem('sketchTracks', JSON.stringify(mouseTracks));
  localStorage.setItem('sketchSize', JSON.stringify({
    width: originalCanvasWidth,
    height: originalCanvasHeight
  }));
  // 打开专门的回放页面
  window.open('/sketch-replay.html', '_blank');
};

在sketch-replay.html中读取数据并绘制:

<!DOCTYPE html>
<html>
<body>
  <script>
    window.onload = () => {
      // 读取数据
      const imgSrc = localStorage.getItem('sketchImg');
      const tracks = JSON.parse(localStorage.getItem('sketchTracks'));
      const { width: originalWidth, height: originalHeight } = JSON.parse(localStorage.getItem('sketchSize'));
      
      // 绘制逻辑和方案1一致,创建img、canvas并绘制轨迹
      
      // 清理临时数据
      localStorage.removeItem('sketchImg');
      localStorage.removeItem('sketchTracks');
      localStorage.removeItem('sketchSize');
    };
  </script>
</body>
</html>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 01:23:19