【问题标题】:How to animate Path2D objects on canvas如何在画布上为 Path2D 对象设置动画
【发布时间】:2020-04-29 15:56:45
【问题描述】:

在我的画布上,我有两种类型的圆圈:

我直接从上下文中创建的那些(= decoratingStars):

 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();

    }

以及那些创建为 Path2D 的,因为我希望它们是可点击的 (=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);
        }

    };

现在我想使用 requestAnimationFrame 为这些圆圈设置动画。我的问题是this.ctx.clearRect(0, 0, WIDTH_CANVAS, HEIGHT_CANVAS); 似乎对 Path2D 对象没有影响,而它对其他对象有效。

还有其他方法可以清除 Path2D 对象吗?

编辑,这里是我的 updateCanvas 方法,生成动画:

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);

    }

【问题讨论】:

    标签: reactjs canvas html5-canvas requestanimationframe path-2d


    【解决方案1】:

    您错误地使用了Path2D 对象。

    drawPlanets 函数中,您每次调用 draw 时都会向路径添加子路径。

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

    因此,每次使用this.ctx.fill(planetPaths[i].ref); 绘制路径时,都会绘制到那时添加的所有子路径。

    使用Path2D

    • 您无法清除Path2D 对象。
    • 您只需添加一次要渲染的路径。
    • 要更改路径,您需要创建一个新的Path2D 或在渲染时转换路径。

    修复

    一次创建路径和所有不变的状态(初始化),然后多次渲染(每一帧)

    您的代码应该看起来更像...

    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);
        }
    }
    

    【讨论】:

    • 感谢您回答我的问题。我正在尝试实施您的解决方案,但我不确定是否完全理解它。我可以成功地用你的代码替换我的代码,并在我的画布上获得相同的绘图。现在,当我想为行星设置动画时,我的方法基本上是修改每个 Path2D 对象的 x 和/或 y 值(请参阅上面的编辑)。这是要走的路吗?
    • @JulienBerthoud 如果您更改路径形状,您必须创建一个新的Path2D。如果您只是更改位置,则可以使用ctx.setTransformctx.translate 或创建新路径并添加带有定位它的DOMMatrix 的原始路径。或者从头开始创建一个新的 path2D。当单行可以做同样的事情时,接缝对于圆形点击区域的工作量很大if (Math.hypot(mouse.x - planet.x, mouse.y - planet.y) &lt; planet.radius) { /* mouse is over the planet */ }
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-05
    • 2013-09-08
    • 1970-01-01
    • 2012-05-22
    • 2013-01-16
    • 2014-12-07
    相关资源
    最近更新 更多