Javascript 使用requestAnimationFrame绘制圆弧

Javascript 使用requestAnimationFrame绘制圆弧,javascript,jquery,canvas,requestanimationframe,Javascript,Jquery,Canvas,Requestanimationframe,我在做什么:我正在使用设定的间隔从圆弧绘制一个圆环。绘制完一个圆后,我正在绘制另一个半径稍大的圆,如fiddle中所示 我想做什么:我想实现相同的功能,但使用requestAnimationFrame并避免设置间隔,一个圆圈应该在1分钟(即60秒)完成 我知道皇家空军是什么,但无法实施 60秒……英国皇家空军……圆圈。?? 我的代码: 仅更新了Ken的问题: 1) 将所有内容乘以2(即刻度)如何使所有内容更清晰 2) 我无法理解设定间隔(anim,120)//这部分..比率..这就是为什么圆圈在

我在做什么:我正在使用设定的间隔从圆弧绘制一个圆环。绘制完一个圆后,我正在绘制另一个半径稍大的圆,如fiddle中所示

我想做什么:我想实现相同的功能,但使用requestAnimationFrame并避免设置间隔,一个圆圈应该在1分钟(即60秒)完成

我知道皇家空军是什么,但无法实施

60秒……英国皇家空军……圆圈。?? 我的代码:

仅更新了Ken的问题

1) 将所有内容乘以2(即刻度)如何使所有内容更清晰

2) 我无法理解设定间隔(anim,120)//这部分..比率..这就是为什么圆圈在60秒完成的原因吗?再一次,我对使用setInterval总是持怀疑态度。原因是它确实会在一段时间后提供震动。但不是在这种情况下。我不想让我的动画停止,这在使用RAF时总是发生。但RAF对优化非常有用。所以有一点困惑,但我想我会按照setInterval的方式进行

3) 这个问题有点难,我现在正在研究。如果我做不到,我会接受你的建议。它涉及一些json,创建多个实例,并在数据停止时停止动画。我明天也会尝试

谢谢你的回答,肯!!!正是我想要的

重复调用函数时,将其调度为只调用一次。如果希望重复调用函数,可以在函数末尾再次调用requestAnimationFrame

下面是一个基于您的代码的示例。请注意如何两次调用requestAnimationFrame:

  • 在最底层,这是第一次调用drawFrame
  • 在drawFrame函数的末尾。drawFrame完成其业务后,调用requestAnimationFrame在下一个动画帧时再次调用自身
  • 请注意,requestAnimationFrame不能保证每秒精确调用函数60次。从(我的)重点:

    当您准备在屏幕上更新动画时,应该调用此方法。这将要求在浏览器执行下一次重新绘制之前调用动画函数。前台选项卡的重新绘制频率最高可达每秒60次(具体速率由浏览器决定),但后台选项卡的重新绘制频率可能会降低

    如果您需要在一秒钟内完成圆弧,请确保根据经过的时间计算圆弧的结束角,而不是根据调用drawFrame的次数计算圆弧的结束角。

    重复调用函数时,请将其安排为仅调用一次。如果希望重复调用函数,可以在函数末尾再次调用requestAnimationFrame

    下面是一个基于您的代码的示例。请注意如何两次调用requestAnimationFrame:

  • 在最底层,这是第一次调用drawFrame
  • 在drawFrame函数的末尾。drawFrame完成其业务后,调用requestAnimationFrame在下一个动画帧时再次调用自身
  • 请注意,requestAnimationFrame不能保证每秒精确调用函数60次。从(我的)重点:

    当您准备在屏幕上更新动画时,应该调用此方法。这将要求在浏览器执行下一次重新绘制之前调用动画函数。前台选项卡的重新绘制频率最高可达每秒60次(具体速率由浏览器决定),但后台选项卡的重新绘制频率可能会降低


    如果需要在一秒钟内完成圆弧,请确保根据经过的时间计算圆弧的结束角,而不是调用drawFrame的次数。

    要生成一个动画,用1分钟画一个圆,没有必要使用rAF,因为这只会产生额外的负载,尽管我个人建议在大多数情况下使用rAF

    然而,在这样的情况下,监视器同步不是那么关键
    setInterval
    (和
    setTimeout
    )可能是更好的负载选择

    下面是修改后的代码,它每分钟绘制一个圆。它基于实际的时间戳,因此计时非常准确。此处的间隔设置为120 ms,但这实际上应该与圆的周长有关,因为这将确定在该时间帧内绘制多少像素,因为重叠像素将不那么可见(忽略此处的子像素)。您可以根据需要自由调整暂停时间

    现在的设置如下(fiddle上不需要window.onload,所以我删除了它,但如果在最后一页的页眉中加载脚本,当然需要将其放回去)。var名称可能更好,但我保留了一些原始名称:

    start_angle = 1.5 * Math.PI, /// common offset (north)
    end_angle   = 2 * Math.PI,   /// ends full circle in radians
    increase_end_angle = 0,      /// current angle incl. offset
    radius = 50,
    startTime = (new Date()).getTime(),  /// get current timestamp
    diff;                        /// used for timestamp diff
    
    我们还将静态设置移到循环之外,以节省一些CPU周期(实际上设置笔划样式等。如果一直设置,则会产生效果,因此这更为优化)。无需使用
    save
    /
    restore
    ,因为我们在外循环期间没有更改许多其他地方需要的变量:

    context.lineWidth = 6;
    context.lineCap = "round";
    
    主要功能是根据实际时间重置圆圈:

    setInterval(anim, 120); /// 120 for demo, use Ø and time to find optimal timeout
    
    function anim() {
    
        /// calc difference between initial and current timestamp
        diff = (new Date()).getTime() - startTime;
        diff = diff / 60000; /// 60000ms = 60s, now we have [0, 1] fractions
    
        /// final angle
        increase_end_angle = start_angle + end_angle * diff;
    
        /// draw circle
        context.beginPath();
        context.arc(x, y, radius, start_angle, increase_end_angle);
        context.stroke();
    
        /// check diff fraction
        if (diff >= 1) { /// if diff >= 1 we have passed 1 minute
            /// update time and new radius
            startTime = (new Date()).getTime();
            radius += 10; /// add to current radius
        };
    }
    
    理想情况下,您应该清除每个绘制的当前圆,以保持蚂蚁锯齿像素,以获得更平滑的外观,因为顶部的重画最终将由于alpha通道而消除这一点

    当然,这意味着当半径增加时,您需要执行一些额外的步骤,例如将当前内容绘制到后面的画布上,以保留已绘制的圆

    更新:您还可以使画布“高分辨率”,以减少arc方法的积垢
    context.lineWidth = 6;
    context.lineCap = "round";
    
    setInterval(anim, 120); /// 120 for demo, use Ø and time to find optimal timeout
    
    function anim() {
    
        /// calc difference between initial and current timestamp
        diff = (new Date()).getTime() - startTime;
        diff = diff / 60000; /// 60000ms = 60s, now we have [0, 1] fractions
    
        /// final angle
        increase_end_angle = start_angle + end_angle * diff;
    
        /// draw circle
        context.beginPath();
        context.arc(x, y, radius, start_angle, increase_end_angle);
        context.stroke();
    
        /// check diff fraction
        if (diff >= 1) { /// if diff >= 1 we have passed 1 minute
            /// update time and new radius
            startTime = (new Date()).getTime();
            radius += 10; /// add to current radius
        };
    }
    
     canvas.width = wantedWidth * 2;
     canvas.height = wantedHeight * 2;
    
     canvas.style.width = wantedWidth + 'px'
     canvas.style.height = wantedHeight + 'px';