Capture the painted area on the canvas

Viewed 54

hi i want build a drawing app that capture the painted area on the canvas like auto Draw . For this, I considered to take the position of the start and end of the line from the canvas. the startingdrawing function get offsetx/y and save it in elements and in the draw function i saved the last position and new position and save them in elements. And my problem is exactly when I log from elements, it returns undefined values for the initial x and y position, and the second x and y position are returned completely correct. Do you have a solution to get the position of the painting drawn inside the canvas?

enter image description here

import { useEffect, useRef, useState, useLayoutEffect } from "react";
import rough from 'roughjs/bundled/rough.esm';
import Menu from "./Menu";
import "./App.css";

  const DrawingSEC = () => {
    const [elements, setElements] = useState([]);
    const canvasRef = useRef(null);
    const ctxRef = useRef(null);
    const [isDrawing, setIsDrawing] = useState(false);
    const [lineWidth, setLineWidth] = useState(5);
    const [lineColor, setLineColor] = useState("black");
    const [selectedElement, setSelectedElement] = useState(null);

    const [screenSize,
      setScreenSize] = useState();
    const createElement = (x1, y1) => {
      return { points: [{ x: x1, y: y1 }] };
    };
    useEffect(() => {
      const handleResize = () => setScreenSize
        (window.innerWidth);
      window.addEventListener('resize', handleResize);

      handleResize();
      return () => window.removeEventListener
        ('resize', handleResize);
    });
    const Generator = rough.generator();
    useEffect(() => {
      const canvas = canvasRef.current;
      const ctx = canvas.getContext("2d");
      ctx.lineCap = "round";
      ctx.lineJoin = "round";
      ctx.strokeStyle = lineColor;
      ctx.lineWidth = lineWidth;
      ctxRef.current = ctx;

    }, [lineColor, lineWidth]);

    const startDrawing = ({ nativeEvent }) => {
      const { offsetX, offsetY, clientX, clientY } = nativeEvent;

      const element = createElement(offsetX, offsetY);
      ctxRef.current.beginPath();
      ctxRef.current.moveTo(offsetX, offsetY);
      ctxRef.current.lineTo(offsetX, offsetY);
      ctxRef.current.stroke();
      setIsDrawing(true);
      nativeEvent.preventDefault();
      setElements(prevState => [...prevState, element]);
      setSelectedElement(...element,offsetX, offsetY);
    };


    const draw = ({ nativeEvent }) => {
      if (!isDrawing) {
        return;
      }
      const { offsetX, offsetY} = nativeEvent;
      ctxRef.current.lineTo(offsetX, offsetY);
      ctxRef.current.stroke();
      const index = elements.length - 1;
      const { x1, y1 } = elements[index];
      const updateElement = createElement(x1, y1)
      const elementscopy = [...elements]
      elementscopy[index] = updateElement;
      setElements(elementscopy)
      nativeEvent.preventDefault();

    };

    const endDrawing = ({ nativeEvent }) => {

      ctxRef.current.closePath();
      console.log(elements);
      setIsDrawing(false);

    };
    const SetToDraw = () => {
      ctxRef.current.globalCompositeOperation = "source-over";
      console.log("draw is run");
    }
    const SetToErase = () => {
      ctxRef.current.globalCompositeOperation = "destination-out";
      console.log("erase is run");

    }

    return (
      <div className="App">
        <div className="draw-area">
          <Menu
            setLineColor={setLineColor}
            setLineWidth={setLineWidth}
            lineWidth={lineWidth}

          />
          <div>
            <button
              className="text-xl rounded-full
            p-3 hover:bg-light-gray"
              onClick={SetToDraw}>
          
              pen


            </button>
            <button
              className="text-xl rounded-full p-3 hover:bg-light-gray"
              onClick={SetToErase}>
             
              erase

            </button>
            
          </div>

          <canvas
            onMouseDown={startDrawing}
            onMouseUp={endDrawing}
            onMouseMove={draw}
            onMouseLeave={endDrawing}
            ref={canvasRef}
            width={screenSize - 300}
            height={`720px`}
          // id={"canvas"}
          />
        </div>
      </div>
    )
  }

  export default DrawingSEC


and this is my Menu component if you want to run this app :

import React from "react";
import "./App.css";

const Menu = ({ setLineColor, setLineWidth,lineWidth }) => {
return (
    <div className="Menu">
    <label>pen color   </label>
    <input
        type="color"
        onChange={(e) => {
        setLineColor(e.target.value);
        }}
        style={{borderRadius:"50%"}}
    />
    <label>Pen Size  </label>
    <input
        type="range"
        min="3"
        max="20"
        value={lineWidth}
        onChange={(e) => {
        setLineWidth(e.target.value);
        }}
    />
    
    </div>
);
};

export default Menu;

1 Answers

you should edit your function like this :

const startDrawing = ({ nativeEvent }) => {
    const { offsetX, offsetY } = nativeEvent;

    const element = createElement(offsetX, offsetY);
    // console.log(element);
    ctxRef.current.beginPath();
    ctxRef.current.moveTo(offsetX, offsetY);
    ctxRef.current.lineTo(offsetX, offsetY);
    ctxRef.current.stroke();
    setIsDrawing(true);
    nativeEvent.preventDefault();
    setElements(prevState => [...prevState, element]);
    // console.log(elements);
    setSelectedElement(...elements, offsetX, offsetY);
  };
const draw = ({ nativeEvent }) => {
    if (!isDrawing) {
      return;
    }
    const { offsetX, offsetY ,clientX, clientY } = nativeEvent;
    ctxRef.current.lineTo(offsetX, offsetY);
    ctxRef.current.stroke();
    const elementsCopy = [...elements];
    setElements(elementsCopy, true);

  };

Related