こんにちは!フロントエンドの現場を長年歩んできたチーフアーキテクトの私です。
Web制作やプログラミングを学び始めると、次のような壁にぶつかりませんか?
「JavaScriptでアニメーションを作ってみたけれど、なんだかカクカクする…」
「`setInterval`や`setTimeout`で必死に動かしているのに、なぜか滑らかにならない…」
大丈夫ですよ、安心してください。それはあなたのコードの書き方が悪いわけでも、パソコンのパワー不足でもありません。単に、「ブラウザの機嫌(仕組み)とタイミング」を少しだけ知らなかっただけなんです。
今回は、Webブラウザの心臓部であるレンダリングの仕組みと、滑らかなアニメーションの救世主`requestAnimationFrame`(リクエスト・アニメーション・フレーム)について、身近な例えを交えながら優しく紐解いていきましょう。
—
1. パラパラ漫画とブラウザの「お絵描きタイム」
まず、私たちが普段見ているWebページが、画面上でどうやって動いているのかをイメージしてみましょう。
Webブラウザが画面を表示する仕組みは、子供の頃に遊んだ「パラパラ漫画」そっくりです。静止画(ページ)を1秒間に何枚もパラパラとめくることで、まるで動いているように見せかけています。
一般的なパソコンやスマートフォンのディスプレイは、1秒間に60回のペースで画面を新しく描き替えています。これを専門用語で「60Hz(ヘルツ)」や「60fps」と言います。
つまり、ブラウザには「1秒間に60回、お絵描きをするタイムリミット(1フレームあたり約167ミリ秒)」が強制的に与えられているんです。
お買い物レジの行列に例えてみよう
ここで一つ、スーパーのレジの行列を想像してみてください。
- 客(JavaScriptなど): 「この商品のバーコードを読み取って!」「新しいアニメーションの計算をして!」と次々にデータを持ってくる。
- レジ係(ブラウザのレンダリングエンジン): 受け取ったデータを計算し、画面という棚に商品を並べ直す。
もし、客が自分のタイミングで勝手に、しかもバラバラのスピードで商品をレジに投げ込んできたらどうなりますか? レジ係はパニックを起こし、レジの列は大渋滞。商品の並び順がめちゃくちゃになり、レジの処理が間に合わなくなってしまいますよね。これが、Webサイトで起きる「フレーム落ち(カクつき)」の正体です。
—
2. 昔ながらの `setInterval` が嫌われる理由
アニメーションを作ろうとしたとき、多くの人が最初に思い浮かべるのが `setInterval` や `setTimeout` ですよね。
// 1秒間に約60回(1000ミリ秒 ÷ 60 ≒ 16ミリ秒ごと)に動かそうとするコード
setInterval(() => {
// ボックスを右に1ピクセル動かす処理
box.style.left = box.offsetLeft + 1 + ‘px’;
}, 1000 / 60);
一見、これで完璧に動きそうに見えます。しかし、ブラウザの裏側の事情を知る私たちからすると、これは「レジの混雑状況を無視して、自分の腕時計の秒針だけを頼りに大声で商品を投げ込み続ける迷惑なお客さん」なんです。
- ブラウザが裏で別の重い処理(データの読み込みなど)をしていて、今お絵描きどころじゃない瞬間であっても、容赦なくJavaScriptが実行される。
- ディスプレイの更新タイミング(60Hz)と、タイマーのタイミングがズレてしまう。
結果として、ブラウザは「描かなくていいタイミングで無理やり描かされる」ことになり、描画がスキップされて画面がカクカクしてしまうのです。これが、`setInterval`で滑らかなアニメーションを作るのが難しい理由です。
—
3. 救世主! `requestAnimationFrame` との出会い
そこで登場するのが、今回の主役である `requestAnimationFrame` です。長ったらしい名前ですが、意味を分解すると「ねぇブラウザさん、次に画面をお絵描きする(レンダリングする)タイミングが来たら、私にも教えて(リクエストして)!」というお願い機能です。
先ほどのスーパーの例えで言うなら、「レジ係さんが『次のお客さん、どうぞ!』と声をかけてくれたタイミングぴったりに、商品を一つだけレジに出す」という、最高にスマートで協調性のあるやり方です。
実際のコードを見てみましょう
百聞は一見にしかず。実際に `requestAnimationFrame` を使ったアニメーションの基本形を見てみましょう。そのままコピペしてブラウザのコンソールなどで試せるコードです。
このコードの美しいところは、自分で「1秒間に何回」とタイマーを設定する必要がない点です。ブラウザが「今、画面を書き換えるよ!」という最高のタイミング(通常は秒間60回)で関数を呼び出してくれるため、無駄な計算が一切発生せず、驚くほど滑らかにボックスが移動します。
—
4. `requestAnimationFrame` のすごいメリットたち
現場のエンジニアがこのAPIを愛してやまない理由は、滑らかさだけではありません。実務で役立つ大きなメリットが他にもあります。
メリット①:裏タブ(非アクティブ)にいるときは自動でストップ!
もしユーザーが別のタブに移動したり、ブラウザを最小化したりした場合、今見ている画面のパラパラ漫画を描く必要はありませんよね。
`setInterval` だと裏のタブでも動き続けてしまい、無駄にCPUやバッテリーを消耗させますが、`requestAnimationFrame` はユーザーが見ていないときは自動的に処理を休止してくれます。ノートパソコンのバッテリーやスマホの電池持ちを守る、地球にもユーザーにも優しいエコ設計なんです。
メリット②:ブラウザのレンダリングサイクルと完全に同期する
ブラウザが画面を描き替えるときは、大まかに以下のステップを踏んでいます。
1. JavaScriptの実行(位置やスタイルの計算)
2. スタイル計算(CSSOM)
3. レイアウト(配置の決定)
4. ペイント(色や形の描画)
`requestAnimationFrame` は、このステップの一番最初(1. JavaScriptの実行)のタイミングにピタリと合わせて処理を挟み込むように設計されています。だからこそ、計算と描画の息がピッタリ合い、フレーム落ちを防ぐことができるのです。
—
5. まとめ:困ったときはブラウザと手を組もう
Web制作をしていると、「いかに自分のコードで無理やりゴリ押しするか」を考えてしまいがちですが、レンダリングの仕組みを知ると、「ブラウザの機嫌(サイクル)に合わせて、手伝ってもらう」方が圧倒的に楽で美しいコードが書けることに気づきます。
- アニメーションや画面の連続的な更新には、`setInterval` ではなく `requestAnimationFrame` を使う。
- ブラウザのお絵描きタイミング(60fps)にすべてを委ねることで、滑らかでカクつかないUIが手に入る。
- ついでにバッテリーやCPUへの負荷も減る。
最初は少し難しく感じるかもしれませんが、「ブラウザにお願いのバトンを渡しているんだな」というイメージを持っていただければバッチリです。
あなたの書くWebサイトが、今日も滑らかで心地よく動きますように。
それでは、また次の現場でお会いしましょう!チーフアーキテクトでした。

コメント