React/Next.js,以困难的方式平移 SVG

React/Next.js, panning an SVG the hard way

正在尝试在此处制作交互式平面图。已经看到一些答案,大多数转向 D3.js - 如果问题无法解决,也会这样做。

这给了我一些开始写作的指导https://codepen.io/Mamboleoo/pen/dmjYpR/

我现在的代码:

const Scheme1 = function (){
  const svg = useRef(null);
  const [isPanning, setIsPanning] = useState(false);
  const [cursorPos, setCursorPos] = useState({ x:0, y:0 });
  const [newSVGPos, setNewSVGPos] = useState({ x:0, y:0 });
  const [SVGViewbox, setSVGViewbox] = useState({ x:0, y:0, width:2472.3, height: 1467.24 });

  let startPanSVG = (e) => {
    setIsPanning(true);
    setCursorPos({ x:e.clientX, y:e.clientY });
  }

  let panSVG = (e) => {
    if(!isPanning) {
      return;
    }

    e.stopPropagation();
    e.preventDefault();
    let SVGDimensions = e.target.getBoundingClientRect();
    let ratio = SVGViewbox.width/SVGDimensions.width;
    let newX = SVGViewbox.x - ((e.clientX - cursorPos.x) * ratio);
    let newY = SVGViewbox.y - ((e.clientY - cursorPos.y) * ratio);
    setNewSVGPos({ x:newX, y:newY });
  }

  let stopPanSVG = () => {
    setIsPanning(false);
    setSVGViewbox({ ...SVGViewbox, x:newSVGPos.x, y:newSVGPos.y });
  }

  return (
    <svg ref={svg} viewBox={Object.values(SVGViewbox).join(" ")} onPointerDown={startPanSVG} onPointerMove={panSVG} onPointerLeave={stopPanSVG} onPointerUp={stopPanSVG} version="1.1" id="svg1003">
    ...
    </svg>
)}

主要问题是 SVG 在平移过程中不会被鼠标拖动,并且只有在 stopPanSVG 完成后才会进行相当苛刻的移动。可能是由于 useState 分别是异步和触发,或者对重新渲染有限制?用 useEffect 替换它的尝试不太顺利 - 理想情况下我希望它在 startPanSVG 内,所以它只在事件上触发,但这违反了钩子的规则。用钩子包装所述函数会使 e.clientX 未定义,即使以 '?' 为前缀或跳过初始安装也是如此。

也许有更好的选择?可能应该添加 window/document/global 由于项目设置而被禁用 - 例如不能 addEventListener

我相信这应该可以解决您的问题:

let panSVG = (e) => {
  if(!isPanning) {
    return;
  }

  e.stopPropagation();
  e.preventDefault();

  let SVGDimensions = e.target.getBoundingClientRect();
  let ratio = SVGViewbox.width/SVGDimensions.width;
  let newX = SVGViewbox.x - ((e.clientX - cursorPos.x) * ratio);
  let newY = SVGViewbox.y - ((e.clientY - cursorPos.y) * ratio);

  // change the viewport here
  setSVGViewbox({ ...SVGViewbox, x: newX, y: newY });
}

let stopPanSVG = () => {
  setIsPanning(false);
  // don't change viewport here
}