JavaScriptでfpsを計測する

Chrome DevToolsにFPS Meterというのがあって、FPSの推移をプロットしてくれるのだけど、他のブラウザや環境によってはプロットできない、あるいは、FPSの値だけ欲しくてプロットは自分でやりたいということもあるので、簡単に計測用のコードを書いてみた。こんなかんじ。

    class FpsCalculator {
      constructor() {
        this._isRunning = false;
        this._beginTime = Date.now();
        this._prevTime = this._beginTime;
        this._frames = 0;
      }
      start() {
        if (this._isRunning) {
          return null;
        }

        this._beginTime = Date.now();
        this._prevTime = this._beginTime;
        this._frames = 0;

        this._isRunning = true;
        const loop = () => {
          if (!this._isRunning) {
            return null;
          }
          this._update();
          requestAnimationFrame(loop);
        }
        loop();
      }
      stop() {
        this._isRunning = false;
        this._frames = 0;
      }
      _update() {
        this._frames++;
        let prevTime = this._prevTime;
        let time = Date.now();

        if (time > prevTime + 1000) {
          console.log((this._frames * 1000) / (time - prevTime));
          this._prevTime = time;
          this._frames = 0;
        }

        this._beginTime = time;
      }
    }
    const calculator = new FpsCalculator();
    calculator.start();

requestAnimationFrameで描画処理の前に呼び出したい関数を登録できる。今回は、描画したフレームの回数をカウントアップして、前回の結果の出力から1秒経過していれば新しい結果を出力するようにしている。描画処理は、理想的には毎秒約60回おこなわれるので(60fps)、requestAnimationFrameに登録し続けている関数も約60回呼ばれて、60に近い数値がConsoleに出力される。JavaScriptでヘビィな処理をおこなったり、リフロー/レイアウトが頻繁に走ったりすると、この回数が少なくなる(fpsが低下する)。

極端なfpsの低下は表示のカクツキ(ジャンク)を引き起こし、ユーザーに違和感を与えるので、原因を特定してそれを取り除く。