Можно ли узнать, какова частота кадров монитора в javascript?

  • Во-первых, можно ли узнать какова частота кадров монитора/частота обновления в javascript (60 Гц для большинства ЖК-мониторов)?
  • Во-вторых, есть ли способ сказать, выполнить функцию после каждые X кадров?

Несколько человек спросили, зачем мне это нужно. Вот контекст: у меня есть анимация (бесконечный цикл, который отображает один кадр за другим). Выход каждой итерации должен быть синхронизирован с частотой обновления монитора, иначе tearing произойдет. То, как я делаю это прямо сейчас, - это использовать setTimeout(loop, 16) в методе loop. Это своего рода работа. Второй параметр должен быть 1/(частота обновления), и именно поэтому я задал этот вопрос.

Ответ 1

Возможно, вам повезло в современных браузерах, используя window.requestAnimationFrame с тривиальным обратным вызовом, который измеряет время между последовательными вызовами и от того, что вычисляет FPS.

Вы также можете легко пропустить функцию рендеринга каждый n-й вызов, чтобы уменьшить желаемую частоту кадров.

Я привел пример на http://jsfiddle.net/rBGPk/ - математика может быть немного неправильной, но этого должно быть достаточно, чтобы показать общую идею.

Ответ 2

Приведенное ниже решение работает путем измерения количества миллисекунд между двумя последовательными кадрами анимации.

Предупреждение: он часто возвращает неправильный FPS, потому что иногда анимационный кадр пропускается, когда ваш процессор занят другими задачами.

// Function that returns a Promise for the FPS
const getFPS = () =>
  new Promise(resolve =>
    requestAnimationFrame(t1 =>
      requestAnimationFrame(t2 => resolve(1000 / (t2 - t1)))
    )
  )

// Calling the function to get the FPS
getFPS().then(fps => console.log(fps));

Ответ 3

Это надежный метод, используя метод requestAnimationFrame.

function calcFPS(opts){
    var requestFrame = window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame;
    if (!requestFrame) return true; // Check if "true" is returned; 
                                    // pick default FPS, show error, etc...
    function checker(){
        if (index--) requestFrame(checker);
        else {
            // var result = 3*Math.round(count*1000/3/(performance.now()-start));
            var result = count*1000/(performance.now()- start);
            if (typeof opts.callback === "function") opts.callback(result);
            console.log("Calculated: "+result+" frames per second");
        }
    }
    if (!opts) opts = {};
    var count = opts.count||60, index = count, start = performance.now();
    checker();
}

Чем выше значение count, тем точнее значение FPS, и чем длиннее будет тест FPS.

Дополнительная логика может использоваться для округления до 15/12s, то есть 24, 30, 48, 60 120... FPS.


Здесь скомпилированная версия (с округлением до 3 FPS):

function calcFPS(a){function b(){if(f--)c(b);else{var e=3*Math.round(1E3*d/3/(performance.now()-g));"function"===typeof a.callback&&a.callback(e);console.log("Calculated: "+e+" frames per second")}}var c=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame;if(!c)return!0;a||(a={});var d=a.count||60,f=d,g=performance.now();b()}

Используется так:

calcFPS(); // Only logs to console (you can remove the console log,
           // making this call redundant)

calcFPS({count: 30}); // Manually set count (the test should take 500ms
                      // on a 60FPS monitor

calcFPS({callback: useFPS}); // Specify a callback so you can use the
                             // FPS number value

var FPS = 0, err = calcFPS({count: 120, callback: fps => FPS = fps});
if (err) FPS = 30;