【问题标题】:Draw arc with increasing radius?绘制半径增加的弧?
【发布时间】:2013-07-29 15:38:29
【问题描述】:

我正在绘制一个逐渐增加并变成圆形的圆弧。动画完成后(圆弧变成圆形)我想绘制另一个半径增加的圆,前一个圆保持不变,第二个动画继续。

Arc to circle fiddle

画完圆圈后,它被洗掉了,这是我不想要的,继续第二个动画。 完成后会出现一些不必要的动画。

我该怎么办?

我的代码:

    setInterval(function(){
        context.save();
        context.clearRect(0,0,500,400);
        context.beginPath();
        increase_end_angle=increase_end_angle+11/500;
        dynamic_end_angle=end_angle+increase_end_angle;
        context.arc(x,y,radius,start_angle,dynamic_end_angle,false);
        context.lineWidth=6;
        context.lineCap = "round";
        context.stroke();
        context.restore();
           if(dynamic_end_angle>3.5*Math.PI){  //condition for if circle completion
                draw(radius+10);//draw from same origin and increasd radius
           }
    },66);

window.onload=draw(30);

更新:我什么时候应该清除间隔以节省一些 cpu 周期,为什么动画在第三圈变慢??

【问题讨论】:

    标签: javascript jquery animation canvas html5-canvas


    【解决方案1】:

    你的代码中的这个 sn-p 有一些缺陷。

    if(dynamic_end_angle>3.5*Math.PI){  //condition for if circle completion
        draw(radius+10);//draw from same origin and increased radius
    }
    

    draw() 的递归调用将在第一个圆圈完全绘制后继续运行。这就是为什么性能会立即变慢的原因。你需要以某种方式阻止它。

    我做了一个简单的修复,如果您愿意,可以对其进行润色。 FIDDLE DEMO

    我的解决方法是删除 context.clearRect(0, 0, 500, 400); 并将新的圆形绘制逻辑更改为:

    if (dynamic_end_angle > 3.5 * Math.PI) { //condition for if circle completion
        increase_end_angle = 0; // this will prevent the draw() from triggering multiple times.
        draw(radius + 10); //draw from same origin.
    }
    

    在这个 stackoverflow thread 中,它提到了如何使其更流畅。您最好使用一些绘图框架,因为优化需要大量工作。

    【讨论】:

    • 我什么时候应该清除间隔以节省一些 cpu 周期,为什么动画在第三圈变慢??
    • @ColeJohnson:我应该清除间隔吗?我应该怎么做才能使动画更清晰..如果没有清晰的矩形,我的动画会变得丑陋并失去清晰度
    • 该修复不会阻止draw() 多次触发,因为increase_end_angle = increase_end_angle + 11 / 500;
    【解决方案2】:

    首先,关于闪烁:您正在使用setInterval,而不是为下一个draw() 清除它。就是这样。

    但我会使用completely different approach;只需检查自开始以来经过的时间,并使用循环绘制适当数量的圆圈。

    var start = new Date().getTime();
    
    var timePerCircle = 2;
    var x = 190, y = 140;
    
    function draw() {
        requestAnimationFrame(draw);
        g.clearRect(0, 0, canvas.width, canvas.height);
    
        var t = (new Date().getTime() - start) / 1000;
        var circles = t / timePerCircle;
        var r = 30;
    
        do {
            g.beginPath();
            g.arc(x, y, r, 0, Math.PI * 2 * Math.min(circles, 1));
            g.stroke();
            r += 10;
            circles--;
        } while(circles > 0);
    }
    
    draw();
    

    【讨论】:

    • 我有一些 json 数据,我必须根据这些数据绘制我的圆圈?不是根据日期..+1
    • @user2617915:好的,所以不要使用时间,而是使用 JSON 中的任何内容。 t 很灵活。
    • @user2617915:我试着让它自我记录。您需要解释哪些部分?
    • 绘图功能...剩下的不多了..我知道 raf 是什么。
    • @user2617915:它以秒为单位获取动画开始后的时间并将其放入t(尽管任何东西都可以在这里输入-t只是一些任意的“动画进度”与timePerCircle 的单位相同)。然后它获取要绘制的圆圈或圆圈部分的数量为circles。使用起始半径(此处为 30),它会在该半径处最多绘制一个圆,然后将半径增加 10 并将剩余的圆数减少 1,直到没有剩余的圆为止。
    【解决方案3】:

    我应该什么时候清除间隔以节省一些 cpu 周期?

    最好不要使用间隔,原因如下:

    • 时间间隔无法同步到监视器的 VBLANK 间隙,因此您会时不时出现抖动。
    • 如果您使用 setInterval,您可能会面临堆叠调用的风险(但在这种情况下风险并不高)。

    更好的方法是您可能已经知道使用requestAnimationFrame。如果当前选项卡/窗口不活动,它的 CPU 消耗更少,能够同步监控并使用更少的资源。

    为什么动画在第三圈会变慢??

    您的绘图调用正在累积,这会减慢一切(setInterval 未被清除)。

    这是一种不同的方法。这是一种简化的方式,并使用差分绘画。

    ONLINE DEMO

    这里的主要绘制函数有两个参数,圆索引和该圆的当前角度。圆的半径存储在一个数组中:

    ...,
    sa = 0,                   // start angle
    ea = 359,                 // end angle
    angle = sa,               // current angle
    oldAngle = sa,            // old angle
    steps = 2,                // number of degrees per step
    current = 0,              // current circle index
    circles = [70, 80, 90],   // the circle radius
    numOfCircles = circles.length, ...
    

    该函数存储旧角度,仅在旧角度和新角度之间绘制一个新段,添加 0.5 以补偿由于抗锯齿、舍入误差等引起的毛刺。

    function drawCircle(circle, angle) {
    
        angle *= deg2rad; // here: convert to radians
    
        /// draw arc from old angle to new angle
        ctx.beginPath();
        ctx.arc(0, 0, circles[circle], oldAngle, angle + 0.5);
        ctx.stroke();
    
        /// store angle as old angle for next round
        oldAngle = angle;
    }
    

    循环增加角度,如果大于或等于结束角度,它将重置角度并增加当前循环计数器。当当前计数器到达最后一个循环时,循环结束:

    function loop() {
    
        angle += steps;
    
        /// check angle and reset, move to next circle        
        if (angle >= ea - steps) {
            current++;
            angle = sa;
            oldAngle = angle;
        }
    
        drawCircle(current, angle);
    
        if (current < numOfCircles)
            requestAnimationFrame(loop);
    }
    

    【讨论】:

      猜你喜欢
      • 2012-07-08
      • 2021-10-16
      • 1970-01-01
      • 1970-01-01
      • 2014-09-29
      • 1970-01-01
      • 2012-01-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多