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 墊片,就不會使用時間戳記值。你已退出候補名單。:)

原因

這是因為好的 rAF 能協助你取得理想中的每秒 60 fps,而 60 fps 則須達到每影格 16.7 毫秒。不過,如果以整數毫秒為單位進行測量,表示我們觀察和指定的所有項目都會以 1/16 為精確度。

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

如上圖所示,藍色長條代表在繪製新影格 (以 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 並未支援 Date.now()

特徵偵測

如果您未使用上述模式,只想找出收到的回呼值類型,可以使用以下技巧:

requestAnimationFrame(function(timestamp){

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

    // ...

檢查 if (timestamp < 1e12) 是快速測試,可用來判斷我們要處理的數字有多大。從技術層面來說,如果網頁持續開啟 30 年,就有可能出現誤判。但我們無法測試它是否為浮點數 (而非四捨五入為整數)。請要求足夠的高解析度計時器,這樣您就一定會在某個時間點取得整數值

我們預計在 Chrome 21 中推出這項異動,因此如果您已開始使用這個回呼參數,請務必更新程式碼!