requestAnimationFrame API - 精确到亚毫秒

Ilmari Heikkinen

如果您一直在使用 requestAnimationFrame,会很高兴看到渲染与屏幕的刷新率同步,从而生成尽可能高的保真度动画。此外,在用户切换到其他标签页时,您可以减少 CPU 风扇噪音,并节省电池电量。

不过,该 API 的一部分将发生变化。传递到回调函数中的时间戳将从类似于 Date.now() 的典型时间戳更改为以浮点数(自网页打开以来的毫秒数)的高分辨率测量结果。如果您使用此值,则需要根据以下说明更新代码

需要注意的是,我这里要说的是:

// assuming requestAnimationFrame method has been normalized for all vendor prefixes..
requestAnimationFrame(function(timestamp){
    // the value of timestamp is changing
});

如果您使用的是此处提供的通用 requestAnimFrame shim,则无需使用时间戳值。您现在已经可以开始使用了。:)

原因

原因何在?rAF 可帮助您获得理想的终极 60 fps,而 60 fps 可转换为每帧 16.7 毫秒。但如果用整数毫秒进行测量,则对于我们想要观察和定位的所有内容,我们的精确率为 1/16。

16 毫秒与 16 毫秒整数的图表比较。

如上所示,蓝色条表示在绘制新帧(60fps)之前必须完成所有工作的最长时间。您可能需要执行超过 16 项操作,但对于整数毫秒,您只能以非常零碎的增量安排和测量。这还不够好。

高分辨率计时器能够提供更精确的数值来解决这个问题:

Date.now()         //  1337376068250
performance.now()  //  20303.427000007

Chrome 中当前提供的高分辨率计时器为 window.performance.webkitNow(),该值通常等于传入 rAF 回调的新参数值。一旦符合规范的后续要求,该方法就会舍弃前缀,并可通过 performance.now() 使用。

您还会发现,上述两个值相差不多,数量级。performance.now() 表示从该特定网页开始加载(具体为 performance.navigationStart)以来的浮点数(以毫秒为单位)。

正在使用

剪裁的关键问题是使用以下设计模式的动画库:

function MyAnimation(duration) {
    this.startTime = Date.now();
    this.duration = duration;
    requestAnimFrame(this.tick.bind(this));
}
MyAnimation.prototype.tick = function(time) {
    var now = Date.now();
    if (time > now) {
        this.dispatchEvent("ended");
        return;
    }
    ...
    requestAnimFrame(this.tick.bind(this));
}

解决此问题的修改非常简单... 扩充 startTimenow 以使用 window.performance.now()

this.startTime = window.performance.now ?
                    (performance.now() + performance.timing.navigationStart) :
                    Date.now();

这是一个相当简单的实现,它没有使用带前缀的 now() 方法,还假定支持 Date.now(),但 IE8 中没有此支持。

功能检测

如果您未使用上述模式,只是想确定您获得的回调值类型,则可以使用此方法:

requestAnimationFrame(function(timestamp){

    if (timestamp < 1e12){
        // .. high resolution timer
    } else {
        // integer milliseconds since unix epoch
    }

    // ...

检查 if (timestamp < 1e12) 可以快速测试我们处理的数字有多大。从技术上讲,只有网页连续打开 30 年,才会有误报。但无法测试它是否为浮点数(而不是向下取整为整数)。请求足够多的高分辨率计时器,肯定会在某个时间点获得整数值

我们计划在 Chrome 21 中推出这项变更,因此,如果您已经在使用以下回调参数,请务必更新您的代码!