假设: 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。这也可以在没有任何阻塞的情况下发生,尽管这种情况很少见。

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

通过更广泛的测试,我发现回调之前阻塞的糟糕时间:100 帧中的 1%,无阻塞:约 400 帧中的 0.21645021645021645%,似乎是由打开窗口或用户的其他一些潜在的 CPU 密集型操作引起的.

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

我已经考虑到人们所说的话,但也许我仍然不明白事情是如何运作的。如果这完全符合规范,我会喜欢一些伪代码来巩固我的想法。

更重要的是,如果有人对我如何解决这些问题有任何建议,那就太好了。我唯一能想到的就是带上自己的 performance.now()测量每一帧并使用它——但这似乎有点浪费,让它每帧有效地运行两次,在任何触发事件之上等等。

请您参考如下方法:

传入 requestAnimationFrame() 的时间戳callback 是动画帧开始的时间。在同一帧期间调用的多个回调都接收相同的时间戳。因此,如果 performance.now()在参数值之前返回一个时间,但在那之后并不奇怪。
Here's the relevant specification:

When the user agent is to run the animation frame callbacks for a Document document with a timestamp now, it must run the following steps:

  1. If the value returned by the document object’s hidden attribute is true, abort these steps. [PAGE-VISIBILITY]

  2. Let callbacks be a list of the entries in document’s list of animation frame callbacks, in the order in which they were added to the list.

  3. Set document’s list of animation frame callbacks to the empty list.

  4. For each entry in callbacks, in order: invoke the Web IDL callback function, passing now as the only argument, and if an exception is thrown, report the exception.


所以你已经为下一个动画帧注册了一个回调(假设只有一个)。滴答滴答滴答,BOOM,该动画帧发生的时间:
  • JavaScript 运行时会记下时间并标记现在。
  • 运行时制作已注册动画帧回调列表的临时副本,并清除实际列表(这样,如果需要很长时间以至于下一个动画帧出现,它们就不会被意外调用)。
  • 该列表中只有一件事:您的回调。系统以 now 作为参数调用它。
  • 您的回调开始运行。也许它以前从未运行过,所以 JavaScript 优化器可能需要做一些工作。或者操作系统可能会将线程切换到其他一些系统进程,例如启动磁盘缓冲区刷新或处理一些网络流量,或其他许多事情。
  • 哦,对了,你的回调。浏览器再次获得 CPU 并且您的回调代码开始运行。
  • 您的代码调用 performance.now()并将其与作为参数传入的 now 值进行比较。

  • 因为在步骤 1 和步骤 6 之间可能会经过短暂但不可忽略的时间量,所以 performance.now() 的返回值可能表明已经过去了几微秒,甚至超过几微秒。 这是完全正常的行为。


    评论关闭
    IT干货网

    微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!