制作ブログ Web制作マークアップJavaScriptsetTimeout は requestAnimationFrame に変えるべき?

setTimeout は requestAnimationFrame に変えるべき?

requestAnimationFrame をはじめて見たので調べていたら、setTimeout や setInterval は requestAnimationFrame にするべき、のような記事タイトルが多かった。どういうことなのだろうと簡単な調査を行ったのでレビュー。

最初に結論

  • すべての setTimeout や setInterval を requestAnimationFrame に置き換えられるわけではない。requestAnimationFrame は、あくまで「反復処理」しかも「1秒間に30回もしくは60回」の反復処理をする際の話しであることが大前提。たとえば、5秒後に何かしらの処理を実行したい場合や繰り返す処理間隔を任意に指定したい場合は、これまで同様、setTimeout や setInterval を使う
  • setTimeout より requestAnimationFrame のほうが処理間隔が数値的に正確
  • ググると、プレフィックスに判別記述が紹介されていることが多いが、もはや不要

というわけで、この結論に至った経緯を紹介します。

体感レベルでの違いが分からない

簡易的な処理を書いて、10秒実行した結果、数字での違いははっきりあったものの、requestAnimationFrame のスゴさを体感できるほどの違いはなかった。そのため、あえて過去のソースを書き換えるほどではないと判断。

もしかすると電光掲示板のようなデザインであれば、なめらかさに違いが出るかもしれない(未検証)。

そもそも requestAnimationFrame とは?

そもそも、requestAnimationFrame は何か。
ネット上の情報を見ると、一秒間におよそ60回、引数に渡した関数を反復処理する、とある(実際は30回の場合あり、後述)。つまり、1000/60ミリ秒ごとに処理を繰り返すということだ。実際には、一秒間の処理回数は60回と決まっているわけではなく、ブラウザに依存するとのこと。

つまり、requestAnimationFrame の場合、インターバル時間を指定できない。setTimeout や setInterval ではインターバル時間を任意に指定できる。

ソースで書くと似ていることが記法は似ていることがよくわかる。

const sample = () => {
  setTimeout(sample, 1000/60);
}
const sample = () => {
  window.requestAnimationFrame(sample);
}

実際に試した結果

同じネット回線で、10秒間計測して、その間に何回処理をするのか計測してみた。
テストしたコードはこちら
https://sample.simplesimplesdesign.com/requestAnimationFrame/

1秒間に60回処理を実行する想定だから、10秒間では「600」が期待値だ。

requestAnimationFrame setTimeout
macOS12/Chrome92 298 556
iOS14/Safari14 602 579

驚きなのは、macOS12/Chrome92 で、requestAnimationFrameの値が298と期待値の半分であること。1分間に30回の処理という計算になる。

そこで、setTimeout(sample, 1000/30); として測定したところ、287 が返ってきた。requestAnimationFrame の1秒間に30回処理としたら、 requestAnimationFrame は setTimeout より処理回数が理論値に近い(=より正確)ことが分かる。

cancelAnimationFrame

ちなみに、実装レベルでは setTimeout を利用するときには、clearTimeout を併用するが、requestAnimationFrame を利用する場合にも、その処理を途中でキャンセルするメソッドが用意されている。それが、cancelAnimationFrame だ。
使い方はclearTimeoutと同様。

補足: requestAnimationFrameのブラウザ対応状況

ググった情報は、requestAnimationFrameをプリフィックス付きで判別する古い記事が多い。
だがしかし、現状では、よっぽど古いブラウザのケアが必要なければプリフィックス判別はもういらない。

Can I Use…

あらためて結論

  • すべての setTimeout や setInterval を requestAnimationFrame に置き換えられるわけではない。requestAnimationFrame は、あくまで「反復処理」しかも「1秒間に30回もしくは60回」の反復処理をする際の話しであることが大前提。たとえば、5秒後に何かしらの処理を実行したい場合や繰り返す処理間隔を任意に指定したい場合は、これまで同様、setTimeout や setInterval を使う
  • setTimeout より requestAnimationFrame のほうが処理間隔が数値的に正確
  • ググると、プレフィックスに判別記述が紹介されていることが多いが、もはや不要