reactjscanvashtml5-canvasrequestanimationframepath-2d

How to animate Path2D objects on canvas


On my canvas I have 2 types of circles :

those I created directly from the context (= decorativeStars):

 drawDecorativeStar = (decorativeStar) => {
        this.ctx.beginPath();
        this.ctx.arc(decorativeStar.x, decorativeStar.y, decorativeStar.radius, 0, 2 * Math.PI);
        this.ctx.fillStyle = "rgba(254, 255, 242," + decorativeStar.alpha + ")";
        this.ctx.fill();
        this.ctx.closePath();

    }

and those created as Path2D because I wanted them to be clickable (=planetPaths):

createPlanetPaths = (planets) => {
        for (var i = 0; i < planets.length; i++) {
            this.planetPaths[i] = {
                ref: new Path2D(),
                x: Math.random() * WIDTH_CANVAS,
                y: Math.random() * HEIGHT_CANVAS,
                radius: this.getPlanetRadius(planets[i]),
                color: planets[i].color,
            }
        }
    }

drawPlanets = (planetPaths) => {

        for (let i = 0; i < planetPaths.length; i++) {
            planetPaths[i].ref.arc(planetPaths[i].x, planetPaths[i].y, planetPaths[i].radius, 0, 2 * Math.PI);
            planetPaths[i].ref.gradient = this.ctx.createLinearGradient((planetPaths[i].x - planetPaths[i].radius), (planetPaths[i].y - planetPaths[i].radius), 1.02 * (planetPaths[i].x + planetPaths[i].radius), 1.02 * (planetPaths[i].y + planetPaths[i].radius));
            planetPaths[i].ref.gradient.addColorStop(0, planetPaths[i].color);
            planetPaths[i].ref.gradient.addColorStop(1, 'red');
            this.ctx.fillStyle = planetPaths[i].ref.gradient;
            this.ctx.fill(planetPaths[i].ref);
        }

    };

Now i'd like to animate these circles, using requestAnimationFrame. My problem is that this.ctx.clearRect(0, 0, WIDTH_CANVAS, HEIGHT_CANVAS); seem to have no effect on the Path2D objects, while it works for the others.

Is there another way to clear Path2D objects?

EDIT, here my updateCanvas method, to generate the animation:

updateCanvas() {

        this.ctx.clearRect(0, 0, WIDTH_CANVAS, HEIGHT_CANVAS);

        for (let i = 0; i < this.planetPaths.length; i++) {
            var planetPath = this.planetPaths[i];
            if (planetPath.x < WIDTH_CANVAS) {
                planetPath.x += 1;
            } else {
                planetPath.x = 0;
            }
        }


        for (let i = 0; i < this.decorativeStars.length; i++) {
            var star = this.decorativeStars[i];
            if (star.decreasing == true) {
                star.alpha -= star.decreasingIncreasingRatio;
                if (star.alpha < 0.10) { star.decreasing = false; }
            }
            else {
                star.alpha += star.decreasingIncreasingRatio;
                if (star.alpha > 0.95) { star.decreasing = true; }
            }
            // star.x+=0.01;
        }

        this.drawDecorativeStars(this.decorativeStars);
        this.drawPlanets(this.planetPaths);

        this.myReq = requestAnimationFrame(this.updateCanvas);

    }

Solution

  • You are using the Path2D object incorrectly.

    In the drawPlanets function you are adding sub-paths to the path each time you call draw.

    planetPaths[i].ref.arc(planetPaths[i].x, planetPaths[i].y, planetPaths[i].radius, 0, 2 * Math.PI);
    

    Thus every time you draw the path with this.ctx.fill(planetPaths[i].ref); you draw all the sub paths you have added up to that time.

    Using Path2D

    Fix

    Create the path and all unchanging states once (init), then render many times (each frame)

    You code should look more like...

    createPlanetPaths = planets => {
        for (const planet of planets) {
            const path = new Path2D();
            const x = Math.random() * WIDTH_CANVAS;
            const y = Math.random() * HEIGHT_CANVAS;
            const radius =  this.getPlanetRadius(planet);
    
            path.arc(x, y, radius, 0, 2 * Math.PI);
    
            const gradient = this.ctx.createLinearGradient((px - radius), (y - radius), 1.02 * (x + radius), 1.02 * (y + radius));
            gradient.addColorStop(0, planet.color);
            gradient.addColorStop(1, 'red');
    
            this.planetPaths[i] = {path, gradient};
        }
    }
    
    drawPlanets = planetPaths => {
        for (const planetPath of planetPaths) {
            this.ctx.fillStyle = planetPath.gradient;
            this.ctx.fill(planetPath.path);
        }
    }