以有限的帧率使用requestAnimationFrame

15
据我了解,JS的requestAnimationFrame API的用法适用于不需要控制帧速率的情况。但我的使用场景则要求<canvas>以指定的fps间隔更新,该间隔可以在1到25之间(即每秒1到25帧)。那么我是否可以有效地使用rAF来获得它提供的优化? 这个问题与我的问题有相似之处,但那里所接受的答案在那个问题的上下文中几乎没有意义。
我有两种可能的解决方案。第一种涉及使用while循环在回调函数中调用requestAnimationFrame之前暂停脚本的执行一段时间。在我看到这个示例时,它有效地限制了动画的fps,但似乎也减慢了整个选项卡的速度。这仍然是一个好的解决方案吗?第二种选择是在setInterval内调用requestAnimationFrame,如上面链接的问题所述。对我而言,这似乎有点复杂,但这可能是最好的选择?
还是有更好的解决方法吗?

我现在只是在测试一些代码... :) - Barrie Reader
你需要固定帧率来计算东西还是仅用于显示目的? - Yoshi
固定帧率是为了显示目的。 - fredrikekelund
3个回答

14

Yoshi的回答可能是解决这个问题的最佳代码方案。但是,我仍将标记此答案为正确,因为经过一些研究,我基本上发现我的问题无效。requestAnimationFrame确实旨在尽可能保持帧率高,并且优化了动画需要保持一致和平滑的情况。

值得注意的是,您不需要requestAnimationFrame来获得优化(即使rAF被吹捧为很棒的性能提升器),因为浏览器仍会优化常规绘制<canvas>。例如,当一个标签页没有焦点时,Chrome就停止绘制它的画布。

因此,我的结论是这个问题无效。希望这可以帮助任何想知道类似问题的人。


1
我认为你的结论是完全正确的。因此,将其作为被接受的答案可能对未来的读者更有帮助。 - Yoshi

9

这只是一个概念证明。

我们要做的只是设定每秒帧数和每帧之间的时间间隔。在绘制函数中,我们会从当前时间中减去上一帧的执行时间,以检查自上一帧以来经过的时间是否超过了我们的间隔(基于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
你是否考虑加入一些叙述性的内容,解释这段代码是如何工作的,以及为什么它是对问题的答案?这将对提问者和其他读者非常有帮助。请注意不要改变原意,让内容更加通俗易懂。 - Andrew Barber
这里有很多东西要处理,但这是一个相当聪明的解决方案。我可能会尝试将其集成到我的代码中,如果在那之前没有其他事情发生,我会尽快回来并添加一些编辑以提供进一步的解释。 - fredrikekelund
2
我已经添加了一些解释性文本。希望这可以帮助到您。 - yckart

5

你可以尝试以下方法,虽然我不确定这是否更好:

  • 使用requestAnimationFrame在一个不可见的上下文中进行渲染
  • 使用固定的帧率setInterval来更新可见的上下文

例如:

<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>

Demo: http://jsfiddle.net/54vWN/


1
非常感谢您的回答和示例代码,非常感激!然而,我认为我的问题实际上是无效的,所以我觉得我不能将其标记为被接受的答案。不过我已经点赞了,再次感谢 :) - fredrikekelund

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接