requestAnimationFrame [now] vs performance.now()时间差异

时间:2016-07-13 19:34:26

标签: javascript timing requestanimationframe

假设:rAF now时间是在全部触发回调时计算的。因此,在调用该帧的第一个回调之前发生的任何阻塞都不会影响rAF now并且它是准确的 - 至少对于第一次回调而言。

在触发rAF集之前进行的任何performance.now()测量应早于rAF now

测试:记录before任何发生之前的基准时间)。设置下一个rAF。将rAF now和实际performance.now()before进行比较,看看它们有多么不同。

预期结果:

var before = performance.now(), frames = ["with blocking", "with no blocking"], calls = 0;
requestAnimationFrame(function frame(rAFnow) {
  var actual = performance.now();
  console.log("frame " + (calls + 1) + " " + frames[calls] + ":");
  console.log("before frame -> rAF now: " + (rAFnow - before));
  console.log("before frame -> rAF actual: " + (actual - before));

  if (++calls < frames.length) { before = actual; requestAnimationFrame(frame); }
});

// blocking
for (var i = 0, l = 0; i < 10000000; i++) {
    l += i;
}

观察:当帧开始之前存在阻塞时,rAF now时间有时不正确,即使对于第一帧也是如此。有时第一帧的now实际上比记录的before时间早。

是否在帧之前发生阻塞,帧内时间rAFnow通常早于帧前时间before - 即使我设置了rAF < em>在之后,我进行了第一次测量。这也可以在没有任何阻塞的情况下发生,尽管这种情况很少见。

(我在大多数时间都会在第一个阻塞帧上出现时间错误。在其他阻塞帧上出现问题的情况比较少见,但如果尝试运行几次,偶尔会发生错误。)

通过更广泛的测试,我发现在回调之前阻塞的时间很短:100帧中的1%,没有阻塞:来自~400帧的0.21645021645021645%,似乎是由打开窗口或其他一些可能的CPU密集型操作引起的由用户。

所以这是相当罕见的,但问题是这根本不会发生。如果你想用它们做有用的事情,模拟时间,动画等,那么你需要那些时间才有意义。

我已经考虑过人们所说的话,但也许我仍然不理解事情的运作方式。如果这是所有的规格,我会喜欢一些伪代码来巩固它。

更重要的是,如果有人对如何解决这些问题有任何建议,那就太棒了。我唯一能想到的就是每帧都采用自己的performance.now()测量并使用它 - 但它看起来有点浪费,每帧都有效地运行两次,除了任何触发事件之外等等。 / p>

2 个答案:

答案 0 :(得分:7)

传递给requestAnimationFrame()回调的时间戳是动画帧开始的时间。在同一帧期间调用的多个回调都接收相同的时间戳。因此,如果performance.now()在参数值之前返回时间,那将会非常奇怪,但在之后 非常奇怪。

Here's the relevant specification:

  

当用户代理要为带有时间戳的文档文档运行动画帧回调时,它必须运行以下步骤:

     
      
  1. 如果文档对象的hidden属性返回的值为true,则中止这些步骤。 [PAGE-VISIBILITY]

  2.   
  3. 让回调成为文档动画帧回调列表中的条目列表,按照它们添加到列表中的顺序。

  4.   
  5. 将文档的动画帧回调列表设置为空列表。

  6.   
  7. 对于回调中的每个条目,按顺序:调用Web IDL回调函数,将 now 作为唯一参数传递,如果抛出异常,则报告异常。

  8.   

所以你已经为下一个动画帧注册了一个回调(让我们只说一个)。勾选滴答滴答,BOOM,该动画帧的发生时间:

  1. JavaScript运行时会记录现在的时间和标签。
  2. 运行时生成已注册动画帧回调列表的临时副本,并清除实际列表(如果事情花了很长时间以至于下一个动画帧出现,则不会意外调用它们。)
  3. 列表中只有一件事:你的回调。系统调用 now 作为参数。
  4. 您的回叫开始运行。也许以前从未运行过,因此JavaScript优化器可能需要做一些工作。或者操作系统可能会将线程切换到某些其他系统进程,例如启动磁盘缓冲区刷新或处理某些网络流量,或者其他任何一些系统进程。
  5. 哦,对,你的回调。浏览器再次获取CPU并且您的回调代码开始运行。
  6. 您的代码调用performance.now()并将其与作为参数传入的 now 值进行比较。
  7. 由于在步骤1和步骤6之间可能会经过短暂但不可忽略的时间,因此performance.now()的返回值可能表示已经过了几微秒,甚至超过几微秒。 这是完全正常的行为。

答案 1 :(得分:2)

我在chrome上遇到了同样的问题,其中对performance.now ()的调用返回的值高于now

后续回调中传递的window.requestAnimationFrame ()

我的解决方法是使用传递给第一个before 而非而不是now的回调的window.requestAnimationFrame ()设置performance.now ()。似乎只使用两个函数中的一个来测量时间可以保证值的进展。

我希望这可以帮助其他任何因此而受苦的人。