JavaScript | Web API:タイマー・スケジューリング - requestAnimationFrame

JavaScript JavaScript
スポンサーリンク

requestAnimationFrame は「ブラウザに最適なタイミングでアニメを進めてもらう仕組み」

requestAnimationFrame(略して rAF)は、
「アニメーションを滑らかに動かすための、ブラウザ専用のタイマー」 です。

setIntervalsetTimeout でもアニメは作れますが、
それらは「一定間隔で実行するだけ」で、ブラウザの描画タイミングとは無関係です。

一方、requestAnimationFrame

ブラウザが画面を描画する直前に呼ばれる
1 秒間に最大 60 回(60fps)で実行される
タブが非表示のときは自動で止まる(無駄な処理をしない)

という特徴があり、
アニメーションを作るなら最優先で使うべき API です。


setInterval と requestAnimationFrame の違いをまず掴む

setInterval は「時間ベース」、rAF は「描画ベース」

setInterval
「◯ミリ秒ごとに実行」
ブラウザの描画タイミングとは無関係
処理が重いとカクつく

requestAnimationFrame
「次の描画タイミングで実行」
ブラウザが最適なタイミングで呼んでくれる
滑らかで省エネ

つまり、
アニメーションは「描画のタイミングに合わせる」ほうが圧倒的に自然 です。

これが rAF がアニメーションに向いている理由です。


requestAnimationFrame の基本形

一番シンプルな書き方

function update() {
  console.log("描画タイミングで呼ばれた");
  requestAnimationFrame(update);
}

requestAnimationFrame(update);
JavaScript

ポイントは、

requestAnimationFrame(update) を呼ぶと
→ 次の描画タイミングで update が呼ばれる
→ その中でまた requestAnimationFrame(update) を呼ぶ

という「自分で自分を呼び続ける」スタイルです。

これがアニメーションの基本パターンになります。

コールバックには「経過時間」が渡される

rAF のコールバックは、
「今の時刻(ミリ秒)」 を引数として受け取れます。

function update(time) {
  console.log(time); // 0 から始まり、ミリ秒で増えていく
  requestAnimationFrame(update);
}

requestAnimationFrame(update);
JavaScript

この time を使うと、
「時間に応じて位置を変える」などのアニメーションが作りやすくなります。


具体例:四角を右に動かすアニメーション

HTML

<div id="box"></div>

<style>
  #box {
    width: 50px;
    height: 50px;
    background: red;
    position: absolute;
    left: 0;
    top: 50px;
  }
</style>

JavaScript

const box = document.querySelector("#box");
let start = null;

function animate(time) {
  if (!start) start = time;

  const progress = time - start; // 経過時間(ミリ秒)
  const x = progress / 5;        // 5ms で 1px 動く

  box.style.left = `${x}px`;

  if (x < 300) {
    requestAnimationFrame(animate); // まだ動かす
  }
}

requestAnimationFrame(animate);
JavaScript

ここで起きていることは、

アニメ開始時の時刻を記録
経過時間に応じて位置を計算
300px まで動いたら終了

という流れです。

setInterval でやるよりも、
動きが滑らかで、ブラウザの負荷も少ない のが分かります。


requestAnimationFrame の「止め方」

rAF は ID を返す → cancelAnimationFrame で止める

requestAnimationFrame も、
setTimeoutsetInterval と同じく「ID」を返します。

const id = requestAnimationFrame(animate);
cancelAnimationFrame(id);
JavaScript

ただし、アニメーションでは
「コールバックの中で次の rAF を呼ぶ」ことが多いので、
止めるときは「次の rAF を呼ばない」ようにするのが一般的です。

例えば、

let running = true;

function animate() {
  if (!running) return;

  // アニメ処理
  requestAnimationFrame(animate);
}

requestAnimationFrame(animate);

// どこかで停止
running = false;
JavaScript

というように、
フラグで制御する のがよくあるパターンです。


requestAnimationFrame が優れている理由を深掘りする

ブラウザの描画タイミングに同期する

ブラウザは通常、
1 秒間に 60 回(60fps)画面を描画します。

rAF はこの描画の直前に呼ばれるため、

画面の更新と処理がズレない
無駄な再描画が起きない
カクつきが少ない

というメリットがあります。

タブが非表示のときは自動で止まる

setInterval はタブが非表示でも動き続けますが、
rAF は 非表示のときは自動で停止 します。

これにより、

CPU の無駄遣いを防ぐ
バッテリー消費を抑える

という効果があります。

アニメーションを作るなら、
この「自動で止まる」性質は非常にありがたいです。

setInterval の「ズレ問題」が起きない

setInterval は処理が重いと、

本来 16ms ごとに動くはずが
20ms、30ms とズレていく

という問題があります。

rAF は「描画タイミングに合わせる」ので、
ズレが蓄積しにくく、
常に滑らかさを保ちやすい のが特徴です。


具体例:フェードインアニメーション

HTML

<div id="box">Hello</div>

<style>
  #box {
    opacity: 0;
    transition: none;
  }
</style>

JavaScript

const box = document.querySelector("#box");
let start = null;

function fade(time) {
  if (!start) start = time;

  const progress = (time - start) / 1000; // 1秒でフェードイン
  box.style.opacity = Math.min(progress, 1);

  if (progress < 1) {
    requestAnimationFrame(fade);
  }
}

requestAnimationFrame(fade);
JavaScript

CSS の transition を使わず、
rAF だけでフェードインを作る例 です。

時間ベースで計算しているので、
PC の性能に関係なく「1 秒でフェードイン」が実現できます。


初心者として requestAnimationFrame で本当に掴んでほしいこと

requestAnimationFrame は「アニメーション専用のタイマー」
ブラウザの描画タイミングに合わせて呼ばれるので滑らか
タブが非表示のときは自動で止まる(省エネ)
コールバックの中で次の rAF を呼ぶのが基本パターン
時間(time 引数)を使うと、正確なアニメーションが作れる
止めるときは cancelAnimationFrame か、フラグ管理が一般的

まずは、

四角を動かす
フェードインさせる
回転させる

などの小さなアニメーションを作ってみると、
「ブラウザと仲良く動くアニメーション」の感覚がすぐに掴めます。

次のステップとして、
Canvas やゲームループに挑戦すると、
rAF の真価がさらに見えてきます。

タイトルとURLをコピーしました