2D sketches with React and the Canvas API
Tags: webdev, javascript, react
Published: 2022-06-03 18:31:08


by [Isaac Okoro](https://blog.openreplay.com/authors/isaac-okoro)

The Canvas API is a built-in HTML5 API for building and effectively drawing shapes to the screen.  The following definition comes from the [MDN web docs](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API): *The **Canvas API** allows drawing graphics via Javascript and the HTML `<canvas>` element. We can use it for animation, game graphics, data visualization, photo manipulation, and real-time video processing.*

The Canvas API is primarily used to render 2D graphics to the screen. To render 3D graphics, the [WebGL API](https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API) should be used, but we'll focus on 2D work here. The WebGL API can also render 2D graphics because it uses the canvas element.

This tutorial will cover the creation of the following:

- A simple animation 
- A blank canvas where we can draw shapes and graphics


## Properties and methods available in the Canvas API

Some properties and methods are available in the Canvas API, and this section will cover them. The difference between the properties and methods is that the properties set or return a particular color or pattern used in the drawings. In contrast, the methods create the designs used by the properties in the drawing. Some properties and methods include: 

- `fillStyle`: This property specifies the color or pattern used in the drawing.
- `font`: This property specifies the font property for text content
- `strokeStyle`: This property specifies the color used for the stroke.
- `createLinearGradient()`: This method creates a linear gradient that can be used on the canvas.
- `rect()`: This method creates a rectangle on the canvas.
- `fillRect()`: This method draws a filled rectangle.
- `stroke()`: This method draws the path you define.

You can read more about the methods and properties available to the canvas API [here](https://www.w3schools.com/tags/ref_canvas.asp). The complete code for this article can be found [here](https://github.com/IsaacThaJunior/react-and-canvas-api).

## Creating a simple animation 

Navigate to the folder of your choice and run the following command to create a new React application.

```
yarn create react-app <NAME OF YOUR CHOICE>
```

You can clean up the file structure by deleting unnecessary files in the src folder. Create an Animation.jsx file, which will hold the code for the animation. Copy and paste the code below into the file.

```javascript
// Animation.jsx
import React, { useRef } from 'react'

const Animation =() => {
  
  const canvasRef = useRef(null)
  const draw = (ctx, frameCount) => {
    ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
    ctx.fillStyle = 'blue';
    ctx.beginPath();
    ctx.arc(170, 50, 30 * Math.sin(frameCount * 0.05) ** 2, 0, 2 * Math.PI);
    ctx.fill();
  };
  
  return <canvas style={{ width: '700px' }} ref={canvasRef} />;
}

export default Animation
```

We returned a canvas element in the code block above and then imported the useRef hook from React. The useRef hook gives us a way to access the DOM elements without using the `document.getElementById` in React. Next, we created a `draw` function that takes two parameters: the `ctx` and the `frameCount`. `Ctx` is used to assess methods and properties in canvas API, while the `frameCount` counts the frames and provides a timer for animations in canvas. After that, the canvas was cleared, and the color used for the drawing was set. Then, we called the `beginPath()` method, which tells the canvas to start drawing. In the following line, we defined what we wanted to draw, which in this case is an arc. The `arc()` takes in five (5) mandatory parameters and one (1) optional parameter:

- The x-coordinate
- The y-coordinate
- The radius of the circle
- The starting angle in radians
- The ending angle in radians

The optional parameter is the choice to specify if the drawing should be clockwise or counterclockwise. It is set to false by default which means clockwise.

```javascript
useEffect(() => {
  const canvas = canvasRef.current;
  const context = canvas.getContext('2d');
  let frameCount = 0;
  let animationFrameId;
  const render = () => {
    frameCount++;
    draw(context, frameCount);
    animationFrameId = window.requestAnimationFrame(render);
  };
  render();
  return () => {
    window.cancelAnimationFrame(animationFrameId);
  };
}, []);
```

In the code above block, we created a useEffect hook that will handle our application's side effects because we won't be able to run the animation for the application without it. Next, we define the canvas with the useRef and then get the canvas's context which we set as 2D. We provided a render function that takes in the `frameCount` and the `draw` function, and we also provided it with the `animationFrameId`, which we set to the `window.requestAnimationFrame()` method. The `window.requestAnimationFrame()` method then calls the render function recursively

The `window.requestAnimationFrame()` method informs the browser that you want an animation performed. You can read more about the `window.requestAnimationFrame()` method [here](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame). Next, we provide a cleanup function to the useEffect hook. This cleanup function takes in the `window.cancelAnimationFrame()`, which calls and cancels the `animationFrameId`. 

Import the file into App.js, and with that, run `yarn start` to view the animation in the browser.


![Animation](https://blog.openreplay.com/a7653420d820ae34c22adcd3ee8362b3/image01.gif)


## Building a blank canvas that accepts drawings

When building a canvas for accepting drawings or graphics, we have to take note of the mouse movements. We are interested in three (3) of those mouse movements. `onMouseDown`, `onMouseUp`, `onMouseMove`:

- `onMouseDown`: The drawing starts when we hold the mouse down (Holding the mouse's left key or double-tapping when using the trackpad on a laptop).
- `onMouseUp`: The drawing stops whenever we stop holding the mouse down (when we stop holding the left key of the mouse or stop double-tapping when using the trackpad on a laptop).
- `onMouseMove`: The drawing follows the movement of the mouse while the mouse is being held down.

We will write functions for handling those mouse movements. Create another file and name it `Drawing.jsx`. This file will hold the code that will allow us to draw on the canvas.

```javascript
import { useEffect, useRef, useState } from 'react';
export default function Drawing() {
  const [drawing, setDrawing] = useState(false);
  const canvasRef = useRef(null);
  const ctxRef = useRef(null);

  return (
    <>
      <canvas
        onMouseDown={startDraw}
        onMouseUp={stopDraw}
        onMouseMove={draw}
        ref={canvasRef}
      />
    </>
  );
}
```    

In the code block above, we created a ref event for interacting with the canvas, and then we passed the ref to the canvas along with the three mouse movements. The second ref event is for storing the context so that we can use it when drawing on the canvas.

```javascript
const startDraw = ({ nativeEvent }) => {
  const { offsetX, offsetY } = nativeEvent;
  ctxRef.current.beginPath();
  ctxRef.current.moveTo(offsetX, offsetY);
  setDrawing(true);
};

const stopDraw = () => {
  ctxRef.current.closePath();
  setDrawing(false);
};

const draw = ({ nativeEvent }) => {
  if (!drawing) return;
  const { offsetX, offsetY } = nativeEvent;
  ctxRef.current.lineTo(offsetX, offsetY);
  ctxRef.current.stroke();
};

const clear = () => {
  ctxRef.current.clearRect(
    0,
    0,
    canvasRef.current.width,
    canvasRef.current.height
  );
};
```

The code block above holds the functionalities that fire off the various mouse events. In the `startDraw` function, we pulled out the `nativeEvent` from the available browser events. Then we pulled out the `offsetX` and `offsetY` of the mouse event to know the mouse's position at any point on the canvas. We then set the state of the drawing to true. In the `stopDraw` function, we called the `closePath()` method and then set the state of the drawing to true. An `if` check was set in the `draw` function, which returns immediately whenever there is no drawing on the canvas. A `clear` function was added to enable the clearing of the canvas when done with the drawing.

```javascript
// Import, state and ref events

useEffect(() => {
  const canvas = canvasRef.current;
  // For supporting computers with higher screen densities, we double the screen density
  canvas.width = window.innerWidth * 2;
  canvas.height = window.innerHeight * 2;
  canvas.style.width = `${window.innerWidth}px`;
  canvas.style.height = `${window.innerHeight}px`;
  // Setting the context to enable us draw
  const ctx = canvas.getContext('2d');
  ctx.scale(2, 2);
  ctx.lineCap = 'round';
  ctx.strokeStyle = 'blue';
  ctx.lineWidth = 20;
  ctxRef.current = ctx;
}, []);

// Drawing functionalities
```

We imported the useEffect hook to manage the application's side effects in the code block above. The context used is the 2D context, and we set the properties for the canvas. Import the code in the `App.js` so that it can be seen in the browser

![Drawing](https://blog.openreplay.com/de2071ff035212a38e79390cac9901a9/image02.gif)


## Conclusion

This article taught about the Canvas API. The use of the Canvas API ranges from building signature pads for remote companies to data visualization and real-time video processing. The methods used in this article can be improved upon and used to develop complex animations and even game graphics.

[![newsletter](https://blog.openreplay.com/static/e6b18eda7a327511296a2f27692f9c70/e9957/newsletter.png)](https://newsletter.openreplay.com/)