Search code examples
javascriptreactjsanimationrequestanimationframe

animation become faster and faster after page update [react.js]


I have this easy animation but I have issue. Everytime I update something on page (for example r of arc) animation become faster and faster after each update or after I saved changes in code. Here is my code:

import React, { useEffect, useRef } from "react";

const CanvasPage = () => {
    const canvasRef = useRef(null);
    const x = useRef(0);
    const r = 50
    const v = useRef(1);

    useEffect(() => {
        const canvas = canvasRef.current;
        const context = canvas.getContext("2d");

        //animation function
        const render = () => { 
            window.requestAnimationFrame(render);

            //bouncing left and right
            x.current = x.current + v.current;
            if(x.current > canvas.width){
                v.current = -1;
            }
            if(x.current <= 0){
                v.current = 1;
            }

            context.clearRect(0, 0, canvas.width, canvas.height);
            context.beginPath();
            context.arc(x.current, 75, r, 0, 2 * Math.PI);
            context.stroke();
        };
        render();
    }, []);

    return (
        <div className="Canvas">
            <h1>Canvas page</h1>
            <canvas id="canvas" ref={canvasRef} height="500px" width="500px" />
        </div>
    )
}

export default CanvasPage;

I have tried all topics regarding with this issue sutch a cancelAnimationFrame() but it did not work. Can some one help me please? Thank you very much.


Solution

  • It is most likely because the render function is being triggered on each load. And since requestAnimationFrame call is not being cancelled in your code, previous animation function will keep being triggered.

    Below fix may solve your issue:

        useEffect(() => {
            const canvas = canvasRef.current;
            const context = canvas.getContext("2d");
            const timerIdHolder = {timerId: null};
    
            //animation function
            const render = () => { 
                // let's store the last timerId in our ref object
                timerIdHolder.timerId = window.requestAnimationFrame(render);
    
                //bouncing left and right
                x.current = x.current + v.current;
                if(x.current > canvas.width){
                    v.current = -1;
                }
                if(x.current <= 0){
                    v.current = 1;
                }
    
                context.clearRect(0, 0, canvas.width, canvas.height);
                context.beginPath();
                context.arc(x.current, 75, r, 0, 2 * Math.PI);
                context.stroke();
            };
            render();
    
            // let's return a unmount callback
            return () => cancelAnimationFrame(timerIdHolder.timerId);
        }, []);