【发布时间】:2020-01-01 01:02:48
【问题描述】:
据我了解,requestAnimationFrame 应该尽可能接近浏览器的帧速率,大约为 60fps。为了确保这确实发生,我一直在记录每个 requestAnimationFrame 调用的时间戳,如下所示:
function animate(now){
console.log(now);
window.requestAnimationFrame(animate);
}
window.requestAnimationFrame(animate);
Console.log 数据显示调用始终以大约 0.016674 毫秒的间隔发生,因此表明帧速率约为 60fps(准确地说是 59.9736116108912fps)。
Console.logs(示例数据):
Timestamp FPS (Current - previous) timestamp
------------------------------------------------------------
100.226 59.97361161 0.016674
116.9 59.97361161 0.016674
133.574 59.97361161 0.016674
. . .
. . .
150.248 59.97361161 0.016674
166.922 59.97361161 0.016674
183.596 59.97361161 0.016674
200.27 59.97361161 0.016674
到目前为止,requestAnimationFrame 调用以一致的时间间隔发生,并且没有任何调用滞后/执行太快。
但是,修改 animate() 函数的内容以执行相对更复杂的函数,会导致 requestAnimationFrame 调用不一致。
Console.logs(示例数据):
Timestamp FPS (Current - previous) timestamp
------------------------------------------------------------
7042.73 59.97361161 0.016674
7066.278 42.4664515 0.023548
7082.952 59.97361161 0.016674
7099.626 59.97361161 0.016674
. . .
. . .
17104.026 59.97361161 0.016674
17112.84 113.4558657 0.008814
17129.514 59.97361161 0.016674
17146.188 59.97361161 0.016674
从上面的数据样本中可以看出,时间戳差异和帧率不再稳定,有时发生得太早/太晚,导致帧率不一致。如果 requestAnimationFrame 一直被延迟调用,我会假设由于 JavaScript 的单线程特性,驻留在 animate() 函数中的复杂代码执行时间过长,从而导致 requestAnimationFrame 延迟调用。但是,由于 requestAnimationFrame 有时被调用得太早,因此情况似乎并非如此。
我的代码(骨架):
for (var counter = 0; counter < elements.length; counter ++) //10 elements
{
//some other code
animate(element);
}
function animate(element)
{
// function invocation => performs some complex calculations and animates the element passed in as a parameter
window.requestAnimationFrame(function() { animate(element) } );
}
从上面的代码 sn-p 中可以看出,requestAnimationFrame 在初始 for 循环中为每个元素调用多次。对于每个元素,RequestAnimationFrame 调用也打算无限进行。由于要执行的动画时间要求很高(动画时间在这种情况下非常重要,并且应该尽可能准确),因此 requestAnimationFrame 调用必须始终以一致的时间间隔发生。理想情况下,这些时间间隔应尽可能接近 0.016674 (≈ 60fps)。
关于要执行的动画的一些细节(在每个画布元素上):
我有一个非常具体的情况,我需要根据时间尽可能准确地绘制闪烁/闪烁动画,即画布颜色必须在指定的时间间隔内以一致的速率变化。因此,例如,画布颜色需要保持红色正好 0.025 秒,然后是另一个 0.025 秒,画布颜色设置为蓝色,然后是另一个 0.025 秒,画布为红色,依此类推......(动画应该对于每个元素,无限继续)。我目前的方法涉及跟踪动画循环本身内已过去的帧数(因此,每个 requestAnimationFrame 调用对应于单个帧)。
由于在 60Hz 显示器上无法实现 0.025 秒的精确帧长度,因此每个红/蓝画布周期应该是“近似值”。因此,考虑到 60Hz 显示器,创建一个完整的周期,画布最初是红色,然后是蓝色,总共需要 3 帧(1 秒/60 = 0.01666667 秒 * 3 帧 = 0.05 秒 =>一个完整的红色/蓝色循环所需的持续时间)。将 0.05 秒除以 2 将得到所需的帧长度(即 0.025 秒),但是由于这在 60Hz 监视器上无法实现,因此该周期近似为呈现 2 个红色画布帧,然后是单个蓝色帧(从而形成整个 3 帧周期)。不幸的是,即使考虑到显示器的刷新率,时间也会波动,从而导致不希望的不准确性。
最后的问题:
您能否澄清导致这种不一致的 requestAnimationFrame 行为的原因?
是否可以应用任何优化来确保 requestAnimationFrame 调用以一致的时间间隔执行?
如果我使用其他类型的功能(例如,Web Worker 与 setInterval() 函数结合使用),能否实现更好的计时精度?
【问题讨论】:
-
您应该在
setTimeout中执行您的逻辑,并在requestAnimationFrame中进行视觉更新 -
@GetOffMyLawn 但是 setTimeout 不是很不准确吗?从进行的测试以及一些在线资源来看,情况似乎如此。这同样适用于 setInterval。
-
在回调完成执行后间隔开始
x毫秒,因此当回调被再次调用时,复杂程度不同的任务可能会延迟或加速。 -
您使用什么浏览器来生成这个
113FPS框架?不能复制这个。否则 1 如果有任何东西阻止浏览器,它将无法保持一致的帧速率。 2,取决于您在做什么/导致瓶颈的原因。一些操作可以卸载到 WebWorker,但不是全部。 3 取决于您到底要做什么。现在,您不应该假设任何帧速率,浏览器可能(并且实际上应该)使其适应显示器的帧速率。因此,两个用户使用两个不同的显示器(即使在同一浏览器+计算机上)可能会得到完全不同的结果。 -
当你有这么短的帧率下降并且你不知道为什么时,GC总是一个很好的猜测。
标签: javascript html performance animation requestanimationframe