requestAnimationFrame and Date.now in WebKit
2015-03-03 の一連のツイートがこちら
performance.now() iOS 8.0 で追加されて、iOS 8.1 で削除されてた
— コラーゲンたっぷりさん (@uupaa) 2015, 3月 3
正しくは、「iOS 8.1.1 で」ですね
performance.now() 削除されてたのソース。 WebKit のとこ
https://t.co/gvpnz9Wwk2
— コラーゲンたっぷりさん (@uupaa) 2015, 3月 3
performance.now()の無効化が何故痛いかというと、Date.nowは相当重い部類のAPIという認識なのでアニメーション中には一回も呼びたくないしTimer系を呼ばないように組み上げると速くなる。
その辺の事情を知らない人はバンバン呼んでるけど、あれはよろしくないです
— コラーゲンたっぷりさん (@uupaa) 2015, 3月 3
function tick(timeStamp) { … }
requestAnimationFrame(tick);
でもらえる timeStamp を上手に使いまわすと Date.now() や performance.now() は基本的に不要。これが速くする秘訣です
— コラーゲンたっぷりさん (@uupaa) 2015, 3月 3
で、一晩Macをsleepしておくと、requestAnimationFrame からもらえる timeStamp と Date.now() の差分が凄いズレるという現象に見舞われてしまって、ぐぬぬってる。Chrome の不具合かもしれないけど
— コラーゲンたっぷりさん (@uupaa) 2015, 3月 3
言いっぱなしも何なので、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") で探すと色々と見つかります。
- http://jsperf.com/window-performance-now-vs-date-now
- http://jsperf.com/new-date-vs-date-now-vs-performance-now/6
OSやブラウザが変わると、比較結果にあまり意味がない ことにも注意してください。
リンク
performance.now と Date.now の関係について、より深く学びたい方は、以下のエントリを参照すると良いでしょう