How to create a drawing app with React

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.

image.png

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.

image.png

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.

  1. Color of the pen
  2. 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. πŸ˜‰

image.png

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;

Connect

Email – [email protected]
https://bio.link/posandu