requestAnimationFrame()のフレームレートを制限してみる

requestAnimationFrame()のフレームレートを制限してみる

アニメーションを実装する際のレンダリング負荷の軽減や環境差をなるべく無くす試行錯誤をした備忘録を残しておきます。

今回は、requestAnimationFrame()のフレームレートを制限する方法です。

環境は以下です。

OS macOS Catalina v10.15.7

参考記事

前提

requestAnimationFrame()を使って作成したアニメーションは、デバイスのリフレッシュレートによって速度が大きく変わります。

警告: アニメーションが 1 フレームでどれだけ進んだかを計算する場合、常に第 1 引数(または現在時刻を取得する他の方法)を使用するようにしてください、そうしないと、アニメーションはリフレッシュレートの高い画面では速く実行されます。これを行う方法については、以下の例を参照してください。

Window.requestAnimationFrame() – mdn

以下のGIFがとてもわかりやすいと思います。

Ensuring Consistent Animation Speedsから引用

結論

fpsを現時点でベターな60で設定しています。

const flamesMap = {
    limit: 60,//制限値
    previousTime: performance.now(),
    currentTime: 0,
    deltaTime: 0,
  };
  let { limit, previousTime, currentTime, deltaTime } = flamesMap;
  const interval = Math.floor(1000 / limit);

  const startAnimation = () => requestAnimationFrame(loopAnimation);
  const loopAnimation = (timestamp) => {
    //フレームレートを制限する
    currentTime = timestamp;
    deltaTime = currentTime - previousTime;
    if (deltaTime <= interval) {
      startAnimation();
      return;
    }
    previousTime = currentTime - (deltaTime % interval);

    //ここに実行したい処理を追加

    startAnimation();
  };
  startAnimation();

また、使用する箇所によって適宜調整するためにclassにしても良さそうです。

class LimitFlames {
    constructor(limit) {
      this.interval = Math.floor(1000 / limit);
      this.previousTime = performance.now();
    }
    isLimitFlames(timestamp) {
      const deltaTime = timestamp - this.previousTime;
      const isLimitOver = deltaTime <= this.interval;
      !isLimitOver &&
        (this.previousTime = timestamp - (deltaTime % this.interval));
      return isLimitOver;
    }
  }
  const limitFlames = new LimitFlames(60);

  const startAnimation = () => requestAnimationFrame(loopAnimation);
  const loopAnimation = (timestamp) => {
    if (limitFlames.isLimitFlames(timestamp)) {
      startAnimation();
      return;
    }

    // 実行したい処理を記述
    console.log("実行したい処理を記述");

    startAnimation();
  };
  startAnimation();

高リフレッシュレートなデバイスも増えてきているのでequestAnimationFrame() を使用する場合は、リフレッシュレートに依存しない実装は必須ですね。
また、以下の記事で紹介されているようなアニメーションの進捗をフレーム処理の外側から管理する方法もあるようです。

Canvasだけじゃない! requestAnimationFrameを使った アニメーション表現

普通のCSS transitionやGSAPともうまく使い分けていきたいです。