requestAnimationFrame API - 精準度現在不到 1 毫秒

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 輔助程式,則未使用時間戳記值。未關切。:)

為什麼要

原因何在?Well rAF 可讓您取得最適當的 60 fps 效能,而 60 fps 能轉換為每個畫格 16.7 毫秒。但若以整數毫秒為單位測量,代表我們可以將精確度設為 1/16,以便觀察及指定的所有內容。

比較 16 毫秒與 16 個整數 ms 的圖表比較。

如上所述,藍色長條代表在繪製新影格之前,必須處理所有工作最多的時間 (每秒 60 個影格)。您可能已經執行超過 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 版中生效,因此如果你已經充分利用這個回呼參數,請務必更新程式碼!