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?
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;
