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

Time:05-09

Trying to do an interactive floor map over here. Already seen some answers, most move on to D3.js - in case the issue is impossible to solve, will do likewise.

This gave me some pointers to start writing https://codepen.io/Mamboleoo/pen/dmjYpR/

The code I have now:

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>
)}

The main issue is the SVG doesn't get dragged with the mouse during the pan, and does a rather harsh move only after stopPanSVG is done. Probably due to useState being asynchronous and triggering respectively, or having a limit on re-renders? Attempts to replace it with useEffect didn't go all too well - ideally I'd want it inside startPanSVG, so it only triggers on event, but that's against the hook's rules. Wrapping said function with the hook makes e.clientX undefined, even when prefixed with '?'s or skipping initial mount.

Perhaps there's a better option? Should probably add that window/document/global are disabled due to project settings - can't addEventListener, for example.

CodePudding user response:

I believe this should fix your problem:

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
}
  • Related