Today, we will be making this drawing app with React.
So let's get started!
Creating the react app
First, we need to create a new react app, so I will run this in the terminal.
npx create-react-app drawing-app
Now this will create a new folder called drawing-app. Now we change our directory.
cd drawing-app
If we run the ls command you should see this.
We're ready to go π
Starting the app
Now we have our app ready, we will start the app.
npm run start
Now open localhost:3000 in your browser. You should see something like this.
Now we open our editor and remove all the code in App.js
and put this code.
// Import dependencies
import { useEffect, useRef, useState } from "react";
// Our code
export default function App() {
return (
<div>
</div>
);
}
First, we will create the canvas.
<canvas></canvas>
Then, we will use the useRef
hook to select the canvas element.
const canvasRef = useRef(null);
.....
<canvas
ref={canvasRef}
></canvas>
Now we have our canvas, now we will add the functionality to draw it. To do this we will create 2 functions one for setting the mouse position and one for drawing. Before we do that, we need to set our states.
const [mouseData, setMouseData] = useState({ x: 0, y: 0 });
const [canvasCTX, setCanvasCTX] = useState(null);
// Set the canvas ctx as the state
useEffect(() => {
const canvas = canvasRef.current; // Select the canvas element
const ctx = canvas.getContext("2d"); // The canvas context
canvas.width = window.innerWidth; // Set width of the canvas to the width of the screen
canvas.height = window.innerHeight;// Set height of the canvas to the height of the screen
setCanvasCTX(ctx); // Finally, set the state
}, [canvasRef]); // Do this everytime the canvas element is changed
The function to save the position
const SetPos = (e) => {
// The e variable is the event
setMouseData({
x: e.clientX, // Mouse X position
y: e.clientY, // Mouse Y position
});
};
Now we add event listeners to our canvas.
<canvas
ref={canvasRef}
onMouseEnter={(e) => SetPos(e)}
onMouseMove={(e) => SetPos(e)}
onMouseDown={(e) => SetPos(e)}
</canvas>
Now every time we move the mouse, the state gets updated.
Function to draw
Now before we create the Draw
function, we need to save 2 states.
- Color of the pen
- Size of the pen
We can use the hook useState
again.
const [color, setColor] = useState("#000000"); // Default color is black
const [size, setSize] = useState(10); // Default size is 10
Now we have our states. Let's create the function now.
const Draw = (e) => {
if (e.buttons !== 1) return; // The left mouse button should be pressed
const ctx = canvasCTX; // Our saved context
ctx.beginPath(); // Start the line
ctx.moveTo(mouseData.x, mouseData.y); // Move the line to the saved mouse location
setMouseData({
x: e.clientX, // Update the mouse location
y: e.clientY, // ^^^^^^^^^^^^^^^^^^^^^^^^^^^
});
ctx.lineTo(e.clientX, e.clientY); // Again draw a line to the mouse postion
ctx.strokeStyle = color; // Set the color as the saved state
ctx.lineWidth = size; // Set the size to the saved state
// Set the line cap to round
ctx.lineCap = "round";
ctx.stroke(); // Draw it!
};
Now we add our event listener to the canvas.
<canvas
ref={canvasRef}
onMouseEnter={(e) => SetPos(e)}
onMouseMove={(e) => {SetPos(e);Draw(e)}}
onMouseDown={(e) => SetPos(e)}
></canvas>
Now save the file and open localhost:3000. Now you can draw some stuff. But wait, we need to add the controls.
<div
className="controlpanel"
style={{
position: "absolute",
top: "0",
left: "0",
width: "100%",
}}
>
<input
type="range"
value={size}
max={40}
onChange={(e) => {
setSize(e.target.value);
}}
/>
<input
type="color"
value={color}
onChange={(e) => {
setColor(e.target.value);
}}
/>
<button
onClick={() => {
const ctx = canvasCTX;
ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
}}
>
Clear
</button>
</div>
Now we have made our drawing app! πThat's all for now. See you next time. π
And if you need to copy-paste the full code here it is.
import { useEffect, useRef, useState } from "react";
function App() {
const [mouseData, setMouseData] = useState({ x: 0, y: 0 });
const canvasRef = useRef(null);
const [canvasCTX, setCanvasCTX] = useState(null);
const [color, setColor] = useState("#000000");
const [size, setSize] = useState(10);
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
setCanvasCTX(ctx);
}, [canvasRef]);
const SetPos = (e) => {
setMouseData({
x: e.clientX,
y: e.clientY,
});
};
const Draw = (e) => {
if (e.buttons !== 1) return;
const ctx = canvasCTX;
ctx.beginPath();
ctx.moveTo(mouseData.x, mouseData.y);
setMouseData({
x: e.clientX,
y: e.clientY,
});
ctx.lineTo(e.clientX, e.clientY);
ctx.strokeStyle = color;
ctx.lineWidth = size;
// Set the line cap to round
ctx.lineCap = "round";
ctx.stroke();
};
return (
<div>
<canvas
ref={canvasRef}
onMouseEnter={(e) => SetPos(e)}
onMouseMove={(e) => SetPos(e)}
onMouseDown={(e) => SetPos(e)}
onMouseMove={(e) => Draw(e)}
></canvas>
<div
className="controlpanel"
style={{
position: "absolute",
top: "0",
left: "0",
width: "100%",
}}
>
<input
type="range"
value={size}
max={40}
onChange={(e) => {
setSize(e.target.value);
}}
/>
<input
type="color"
value={color}
onChange={(e) => {
setColor(e.target.value);
}}
/>
<button
onClick={() => {
const ctx = canvasCTX;
ctx.clearRect(
0,
0,
canvasRef.current.width,
canvasRef.current.height
);
}}
>
Clear
</button>
</div>
</div>
);
}
export default App;