latest log

酩酊状態で書いたエンジニアポエムです。酩酊状態で読んでください。

requestAnimationFrame and Date.now in WebKit

2015-03-03 の一連のツイートがこちら

正しくは、「iOS 8.1.1 で」ですね

言いっぱなしも何なので、WebKit (Safari, MobileSafari) における requestAnimationFrame, performance.now(), Date.now() がどのように実装されているか拾ってみました。

  • performance.now() も Date.now() も最終的には currentTime 関数に辿り着きました
  • currentTime 関数は 各OSが提供する時刻取得APIを呼び出すことで現在時刻を取得しています
  • Date.now() は、1970年を起点とした現在時刻を返すAPIです
    • この値は、floor(currentTime() * 1000.0) で計算できます
  • requestAnimationFrame() のコールバック関数には引数(timeStamp)が渡されます
    • この値は、currentTime() - performance.timing.navigationStart で計算できる、ブラウザの起動時を0とする、ミリセカンド.ナノセカンド の経過時間です
    • requestAnimationFrame でコールバック関数が複数登録されている場合でも、timeStamp の計算は一度きり。効率的です。

requestAnimationFrame + timeStamp と performance.now を使った効率的なアニメーションについて

以下は、無駄のないようにアニメーションの骨組みを実装した例です。

var lastTimeStamp = performance.now();

function tick(timeStamp) {
    var deltaTime = timeStamp - lastTimeStamp;

    requestAnimationFrame(tick);
}

requestAnimationFrame(tick);

以下は、currentTime() が何度も無駄に呼ばれてしまう非効率なアニメーションの実装例です。

var lastTimeStamp = Date.now();

function tick() {
    var deltaTime = Date.now() - lastTimeStamp;

    requestAnimationFrame(tick);
}

requestAnimationFrame(tick);

timeStamp を使わずに、Date.now() - lastTimeStamp で deltaTime を求めるやり方はお勧めできません。
内部では currentTime() 関数が最低でも2回呼ばれてしまいますし、requestAnimationFrame にコールバック関数が複数登録されている場合は、その回数分 Date.now() を実行してしまいます。

Date.now vs performance.now

Date.now と performance.now ではどっちが速いの? という疑問もあるでしょう。

var perf = window.performance;

perf.now();
Date.now();

Google.search("jsperf performance.now Date.now") で探すと色々と見つかります。

OSやブラウザが変わると、比較結果にあまり意味がない ことにも注意してください。

リンク

performance.now と Date.now の関係について、より深く学びたい方は、以下のエントリを参照すると良いでしょう