javascript - 用requestAnimationFrame绘制弧

标签 javascript jquery canvas requestanimationframe

FIDDLE

我正在做什么:我使用设定的间隔从圆弧中绘制出圆弧。绘制完一个圆之后,我正在绘制另一个圆度稍大的圆,如小提琴所示。

我想做的事情:我想实现相同的功能,但是应该使用requestAnimationFrame并避免setInterval在1分钟(即60秒绘制一个圆)上完成一个圆。

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

60秒...皇家空军...圈子。
我的代码:

    //for full code see the 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
        increase_end_angle = 0;
        draw(radius + 10); //draw from same origin.
    }
}, 3);


仅针对Ken更新了问题:

1)如何将所有内容乘以2(即比例)使所有内容更加清晰。

2)我不明白setInterval(anim,120); //这部分..比率..这就是为什么圆在60秒时完成吗?再说一次,我总是怀疑使用setInterval.Reason确实会在一段时间后产生混响。虽然不是在这种情况下。我想让我的动画停止,这在使用RAF时总是会发生。但是raf对于优化来说还是很棒的。那里有些混乱,但是我想我会按照setInterval的方式进行。

3)这个问题有点困难,我现在正在研究它。万一我做不到,我会听你的建议。它处理一些json,创建多个实例并在数据停止发送时停止动画我明天会尝试,现在太累了。

感谢您的回答ken!正是我想要的。

最佳答案

要制作将使用1分钟绘制一个圆的动画,无需使用rAF,因为这会产生额外的负载,即使我个人建议在大多数情况下使用rAF。

但是,在这样的情况下,监视器同步不是很关键的setInterval(和setTimeout)在负载方面可能是更好的选择。

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

Modified fiddle here

现在的设置如下(在小提琴上不需要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通道而将其消除。

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

更新:您还可以通过设置画布使画布“高分辨率”以减少圆弧方法的粗糙性:

 canvas.width = wantedWidth * 2;
 canvas.height = wantedHeight * 2;

 canvas.style.width = wantedWidth + 'px'
 canvas.style.height = wantedHeight + 'px';


只要记住相应地缩放所有坐标和大小即可(x2)。

Updated fiddle running with high-resolution canvas

更新:解决其他问题:


  1)如何将所有内容乘以2(即比例)使所有内容更加清晰。


在“高分辨率模式”下发生的事情是,我们使用的画布是原始画布尺寸的两倍,但是通过应用额外的样式(CSS),我们将整个画布缩小到第一尺寸。

但是,现在在同一区域中的像素数目是原来的两倍,由于子像素化,我们可以利用它来获得更好的“分辨率”。但是,与此同时,我们还需要缩放所有内容以使其恢复到与使用双倍尺寸画布之前相同的位置。

就像是假设图片为400x400的图片。如果以400x400显示,则像素比率为1:1。如果您改用800x800的图像,但将其尺寸减小到400x400,则图像中仍将有800x800像素,但是将无法显示的像素(因为监视器无法显示一半像素)进行插值以使其显示在此处有一半像素。

但是,对于形状,您可以实现形状的更好的详细版本,因为首先对其进行抗锯齿,然后对其周围进行插值,使其看起来更平滑(如在演示中所见)。


  2)我知道了setInterval(anim,120); //这部分..ratio..is
  这就是为什么圆圈在60秒后完成?再一次,我总是
  使用setInterval.Reason时令人怀疑,它确实提供了混蛋
  一段时间后。虽然不是在这种情况下。我希望我的动画能够
  停下来,这在使用RAF时总是会发生。
  进行优化。所以有点混乱,但是我想我会去的
  通过setInterval的方式。


首先出现这种情况是由于setIntervalsetTimeout无法同步到显示器的VBLANK。当扫描内部显示屏的光束开始出现新的帧时,VBLANK来自旧的CRT电视。要正确同步,请同步到VBLANK间隙。这适用于与视频相关的所有设备,包括视频。电脑。

但是,由于这将需要计时器的浮动分辨率(在60Hz的情况下为16.7ms),因此这些计时器无法实现。在这之间的时间不时,您会得到舍入错误,这会导致帧在循环中突然跳动,从而导致跳动。内置了rAF(requestAnimationFrame),它可以同步到显示器的刷新率,但不仅是因为这个原因。它更底层,更高效,因此也可以减少功耗。

如果您不需要监视器同步(每秒最多60次),则使用不准确的计时器也没什么错-在这种情况下,您更依赖在一整分钟内绘制的圆的半径。如果您使用了rAF,则在此期间您可能会多次绘制同一像素,而看不到任何变化(尽管子像素会加入,但对于某些像素仍会产生很小的视觉变化)。因此,这里的rAF并没有达到目的,因为您会进行许多不必要的绘制,而这些绘制不会在屏幕上产生影响。

setInterval对于每个SE来说还不错,但是对于动画来说,在需要不断更新的地方通常太不准确了。它要做的是创建一个事件并将其放入浏览器的事件队列中。只要有可能,事件就会在它超时的时间执行(队列包含许多事件,例如重绘,函数调用等)。这里不是超级准确,但足够精确,因为我们不依赖于超时时间,而是使用更新弧线时的实际时间。因此,为此目的,当我们以很小的增量进行绘制时,将可以掩盖细微的错误。

这并不意味着其他因素可能会在更新中引发混乱,但这也适用于rAF。没有什么是完美的。

至于120毫秒的时间-这只是我开始的数字。这个数字并没有太大地改变平滑度。然而,这并不是使圈子完整的原因。

完整的圆圈是时间"Now time" - "Start time"的差。在这种情况下,这将相差60,000毫秒,因为我们每轮使用60秒。因此,要得到一个有用的数字,我们要除以60000,这样我们得到的数字就在0.0到1.0之间,当diff为1时,我们可以直接与该角度相乘以获得100%的角度。

理想的计时器是相对于圆的周长使用时间。这将决定每个新像素之间有多少时间,因此您可以例如再次将其除以10,以考虑子像素化。这将是最佳选择,因为更新的终点不会有重叠,但是每次触发循环时,都会绘制一个新像素。


  3)这个问题有点困难,我正在努力
  现在。如果我做不到,我会接受你的建议。
  与一些JSON,创建多个实例并停止动画
  当数据停止传来时,我明天会尝试,现在太累了。


为此,我建议打开一个新问题。而且,元答案似乎超出了主要答案。:-o :-)

关于javascript - 用requestAnimationFrame绘制弧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18037177/

相关文章:

javascript - tinymce:在左侧对齐对话框窗口的页脚按钮?

html - 如何缩小图像以适合 Canvas 而不扭曲图像?

javascript - 对样式组件概念的误解

javascript - Jasmine/PhantomJS 忽略 __defineGetter__ 方法

Jquery更改按钮的文本和图标

javascript - jQuery 中的全宽图库

javascript - 将织物图像插入 Canvas 上

javascript - A* 在 HTML5 Canvas 中开始寻路

javascript - less/css - 使用值为 (n) 的数据属性定位 dom 元素

Javascript,计算后出现结论