Подтвердить что ты не робот

RequestAnimationFrame с ограниченной частотой кадров

Как я понимаю, использование API JS requestAnimationFrame предназначено для случаев, когда частоту кадров не нужно контролировать, но я использую случай, когда важно, чтобы <canvas> обновлялся только на определенный интервал fps, который может быть где-то между 1 и 25 (от 1 до 25 кадров в секунду, то есть). Могу ли я каким-то образом эффективно использовать rAF для получения оптимизаций, которые он предлагает?

Этот вопрос имеет сходство с моим, но принятый ответ там приблизился к нулю для меня в контексте этого вопроса.

У меня есть два возможных решения. Первый включает использование цикла while для остановки выполнения script для указанной задержки перед вызовом requestAnimationFrame из обратного вызова. В примере, где я видел это, он фактически ограничивал fps анимации, но также, казалось, замедлял всю вкладку. Действительно ли это на самом деле хорошее решение? Второй вариант, как упоминается в вопросе, который я связал выше, вызывает requestAnimationFrame в пределах setInterval. Для меня это кажется немного запутанным, но может быть, это лучший вариант?

Или есть ли лучшая альтернатива для этого?

4b9b3361

Ответ 1

Ответ Yoshi, вероятно, является лучшим решением для этой проблемы. Но все же я буду отмечать этот ответ как правильный, потому что после некоторого исследования я в основном обнаружил, что мой вопрос недействителен. requestAnimationFrame действительно предназначен для того, чтобы поддерживать частоту кадров настолько высоко, насколько это возможно, и оптимизирует для сценариев, в которых анимация должна быть последовательной и плавной.

Стоит отметить, что вам не нужно requestAnimationFrame, чтобы получить оптимизацию (хотя rAF рекламировался как отличный ускоритель производительности), поскольку браузеры все еще оптимизируют регулярный рисунок <canvas>. Например, когда вкладка не сфокусирована, Chrome за один раз рисует свои холсты.

Итак, я пришел к выводу, что этот вопрос недействителен. Надеюсь, это поможет любому, кто интересуется чем-то похожим на меня.

Ответ 2

Это просто доказательство концепции.

Все, что мы делаем, это установить наши кадры в секунду и интервалы между каждым фреймом. В функции рисования мы вычитаем наше последнее время выполнения кадров с текущего времени, чтобы проверить, прошло ли время, прошедшее с момента последнего кадра, больше нашего интервала (который основан на fps) или нет. Если условие оценивается как true, мы устанавливаем время для нашего текущего кадра, который будет "последним временем выполнения кадра" в следующем вызове рисования.

var Timer = function(callback, fps){
  var now = 0;
  var delta = 0;
  var then = Date.now();

  var frames = 0;
  var oldtime = 0;

  fps = 1000 / (this.fps || fps || 60);

  return requestAnimationFrame(function loop(time){
    requestAnimationFrame(loop);

    now = Date.now();
    delta = now - then;

    if (delta > fps) {
      // Update time stuffs
      then = now - (delta % fps);

      // Calculate the frames per second.
      frames = 1000 / (time - oldtime)
      oldtime = time;

      // Call the callback-function and pass
      // our current frame into it.
      callback(frames);
    }
  });
};

Использование:

var set;
document.onclick = function(){
  set = true;
};

Timer(function(fps){
  if(set) this.fps = 30;
  console.log(fps);
}, 5);

http://jsfiddle.net/ARTsinn/rPAeN/

Ответ 3

Что вы можете сделать, хотя я действительно не знаю, действительно ли это лучше:

  • визуализировать невидимый контекст с помощью requestAnimationFrame
  • обновить видимый контекст с помощью setInterval с использованием фиксированного fps

Пример:

<canvas id="canvas"></canvas>​

<script type="text/javascript">
  (function () {
    var
      ctxVisible = document.getElementById('canvas').getContext('2d'),
      ctxHidden = document.createElement('canvas').getContext('2d');

    // quick anim sample
    (function () {
      var x = 0, y = 75;

      (function animLoop() {
        // too lazy to use a polyfill here
        webkitRequestAnimationFrame(animLoop);

        ctxHidden.clearRect(0, 0, 300, 150);
        ctxHidden.fillStyle = 'black';
        ctxHidden.fillRect(x - 1, y - 1, 3, 3);

        x += 1;
        if (x > 300) {
          x = 0;
        }
      }());
    }());

    // copy the hidden ctx to the visible ctx on a fixed interval (25 fps)
    setInterval(function () {
      ctxVisible.putImageData(ctxHidden.getImageData(0, 0, ctxHidden.canvas.width, ctxHidden.canvas.height), 0, 0);
    }, 1000/40);
  }());
</script>

Демо: http://jsfiddle.net/54vWN/