Animation with Canvas and requestAnimationFrame() in React
Tags: learning, react, showdev, tutorial
Published: 2020-12-10 18:13:51

Hi! So I spent a couple of days trying to figure out how to use `<canvas>` in React.

> Canvas lets you output graphics on the screen 
You may use it for games like Pong or data visualization

As a result I put together a very basic app to use it as a reference in case I need it in a project. Let me go through the code briefly! 

# What you see on the screen

Basically the app is supposed to do three things

1. Output graphics on the screen 
2. Define the movement of the box 
3. Let you pause / resume the animation with a button
 
![canvas_screenshot_1 grey](https://dev-to-uploads.s3.amazonaws.com/i/lczrde5se2hmc26q2onj.png)
 

# What you see in the code

Here is the [source code] (https://github.com/ptifur/react-request-animation-frame). And the working [app] (https://festive-kalam-2a6785.netlify.app).

Essentially the application has three parts. 

```javascript
const CanvasPractice = () => {

    // get canvas
    const canvasRef = useRef()

    // set frame counter
    const [counter, setCounter] = useState(0)
    const [shouldStop, setShouldStop] = useState(true)

    // box position and speed
    const [positionX, setPositionX] = useState(165)
    const [positionY, setPositionY] = useState(165)

    const [dx, setDx] = useState(2)
    const [dy, setDy] = useState(1.5)

    const [motionType, setMotionType] = useState('Circle')

    // update the counter
    useLayoutEffect(() => {
        if (!shouldStop) {
            let timerId

            const animate = () => {
                setCounter(c => c + 1)
                timerId = requestAnimationFrame(animate)
            }
            timerId = requestAnimationFrame(animate)
            return () => cancelAnimationFrame(timerId)
        }
    }, [shouldStop])

    // output graphics
    useEffect(() => {
        
        const canvas = canvasRef.current
        const context = canvas.getContext('2d')

        context.clearRect(0, 0, 350, 350)
 
        // some code to calculate position 

        context.fillStyle = '#555555'
        context.fillRect(positionX, positionY, 20, 20)

    }, [counter])
    
    const changeMotionType = () => {
        if (motionType === 'Circle') {
            setMotionType('Bounce')
        } else {
            setMotionType('Circle')
        }
    }

    return (
        <div className='container'>
            <canvas ref={canvasRef} 
                width="350px" height="350px" 
                onClick={changeMotionType} 
            />
            <h3>Frame count: {counter}</h3>
            <p>Motion type is {motionType}</p>
            <button 
                onClick={() => setShouldStop(!shouldStop)}>
                { shouldStop ? 'Start' : 'Stop' }
            </button>
        </div>
    )
}

export default CanvasPractice
```

## 1. The Engine
`useLayoutEffect()` section serves as an engine. `requestAnimationFrame()` function refreshes itself roughly 60 times a second and increased the counter value.  It's the main pulse of the app.

You feed the counter as a dependency to the `useEffect()` section causing it to refresh and update the graphics on the screen.

### Further reading

Here is a great article explaining how `requestAnimationFrame()` function works

[Using requestAnimationFrame with React Hooks] (https://css-tricks.com/using-requestanimationframe-with-react-hooks/)

And here you may find superuseful notes on why you might prefer useLayoutEffect() over useEffect() for this application

[requestAnimationFrame and useEffect vs useLayoutEffect] (https://blog.jakuba.net/request-animation-frame-and-use-effect-vs-use-layout-effect/)

## 2. Output

In the useEffect() section you initialize the canvas. 

Calculate box position for the current frame, updating `positionX` and `positionY` state.

And output it using the `context.fillRect(positionX, positionY, 20, 20)` method.

## 3. In control
The button lets you pause / resume the animation. 

Then there are two modes in which the box moves. It runs in circles or bounces off the sides of the board, like it would do in a video game. You can switch the mode by clicking anywhere on the board.

# So

Did you find this exploration interesting?
If you have any suggestions, let me know!

# Update

And here is the actual game built on the same foundation
[poung.ptifur.digital](https://poung.ptifur.digital)