Saghar Francis
Saghar Francis

Reputation: 29

Drawing SVG shape with mouse using React

I am using React with typescript. In my app, I converted the logic for drawing SVG using javascript on the existing question on StackOverflow

The problem is my shapes are not drawing. Please help me with what I missed in the code my addRectangle method is not triggering.

Here is my code:

let last_mousex: number = 0;
    let last_mousey: number = 0;
    let mousex: number = 0;
    let mousey: number = 0;
    let mousedown:boolean = false;

    const mouseDown = () => {
        last_mousex = xCord;
        last_mousey = yCord;
        mousedown = true;
    }

    const mouseUp = () => {
        mousedown = false;
    }

    const mouseMove = () => {
        mousex = xCord;
        mousey = yCord;
    }

    const addRectangle = () => {
        if (mousedown) {
            const width = Math.abs(mousex - last_mousex);
            const height = Math.abs(mousey - last_mousey);
            return <rect
          className={"rectangle"}
          x={last_mousex}
          y={last_mousey}
          height={height}
          width={width}
        />
        }
    }

return (
    <div className="App" ref={divRef}>
      <svg
        id="svg"
        ref={svgRef}
        onMouseDown={mouseDown}
        onMouseUp={mouseUp}
        onMouseMove={mouseMove}
      >
        {addRectangle}
      </svg>
    </div>
  );

Upvotes: 1

Views: 1763

Answers (1)

MWO
MWO

Reputation: 2806

You were close, but have not used a component state at all, throwing away and overwriting the coordinates. You can do it like that:

const {useRef, useEffect, useState} = React;

const App = () => {
  const svgRef = useRef(null);
  const divRef = useRef(null);
  const { xCord, yCord } = useMousePosition({ divRef });
  const [mousedown, setMouseDown] = useState(false);
  const [last_mousex, set_last_mousex] = useState(0);
  const [last_mousey, set_last_mousey] = useState(0);
  const [mousex, set_mousex] = useState(0);
  const [mousey, set_mousey] = useState(0);
  const [rectx, setrectx] = useState(0);
  const [recty, setrecty] = useState(0);
  const [rectwidth, setrectwidth] = useState(0);
  const [rectheight, setrectheight] = useState(0);

  const mouseDown = () => {
    set_last_mousex(xCord);
    set_last_mousey(yCord);
    setMouseDown(true);
  };

  const mouseUp = () => {
    setMouseDown(false);
  };

  const mouseMove = () => {
    set_mousex(xCord);
    set_mousey(yCord);
  };

  const addRectangle = () => {
    if (mousedown) {
      const width = Math.abs(mousex - last_mousex);
      const height = Math.abs(mousey - last_mousey);

      const rx = mousex < last_mousex ? mousex : last_mousex;
      const ry = mousey < last_mousey ? mousey : last_mousey;
      if(rectx !== rx)  setrectx(rx);
      if(recty !== ry )setrecty(ry);
      if(rectheight !== height) setrectheight(height);
      if(rectwidth !== width)  setrectwidth(width);

      return (
        <rect
          className={"rectangle"}
          x={rx}
          y={ry}
          height={height}
          width={width}
        />
      );
    }
  };

  return (
    <div className="App" ref={divRef}>
      <svg
        id="svg"
        ref={svgRef}
        onMouseDown={mouseDown}
        onMouseUp={mouseUp}
        onMouseMove={mouseMove}
      >
        {addRectangle() ? (
          addRectangle()
        ) : (
          <rect
            className={"rectangle"}
            x={rectx}
            y={recty}
            height={rectheight}
            width={rectwidth}
          />
        )}
      </svg>
    </div>
  );
}



function useMousePosition({divRef}){
  const [position, setPosition] = useState({ xCord: 0, yCord: 0 });

  useEffect(() => {

    function setFromEvent(e) {
          return setPosition({ xCord: e.clientX, yCord: e.clientY });
      }
      if(divRef.current){
          divRef.current.addEventListener("mousemove", setFromEvent);
      }
      
    return () => {
        if(divRef.current){
            divRef.current.removeEventListener("mousemove", setFromEvent);
        }
    };
  });
  return position;
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
.App { height: 500px; width: 800px; background-color: rgb(126, 119, 119); } #svg { position: absolute; top: 0; left: 0; height: 100%; width: 100%; border: 1px solid #000000; cursor: crosshair; } .rectangle { fill: rgba(255, 255, 255, 0); stroke-width: 3; stroke: rgb(250, 0, 0); }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js" integrity="sha512-qlzIeUtTg7eBpmEaS12NZgxz52YYZVF5myj89mjJEesBd/oE9UPsYOX2QAXzvOAZYEvQohKdcY8zKE02ifXDmA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js" integrity="sha512-9jGNr5Piwe8nzLLYTk8QrEMPfjGU0px80GYzKZUxi7lmCfrBjtyCc1V5kkS5vxVwwIB7Qpzc7UxLiQxfAN30dw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<div id="root"></div>

Upvotes: 1

Related Questions