requestAnimationFrame以有限的帧速率

时间:2012-02-16 09:04:02

标签: javascript

据我了解,JS requestAnimationFrame API的使用是针对帧率不需要控制的情况,但我有一个用例,其中<canvas>至关重要仅以某个fps间隔更新,该间隔可能介于1到25之间(即每秒1到25个,即)。那么我可以以某种方式仍然有效地使用rAF来实现它提供的优化吗?

This question与我有相似之处,但在那个问题的背景下,我接受的答案对我来说几乎没有任何意义。

我有两个可能的解决方案。第一个涉及使用while循环来停止脚本执行指定的延迟,然后再从回调中调用requestAnimationFrame。在我看到这个的例子中,它有效地限制了动画的fps,但它似乎也减慢了整个标签的速度。这仍然是一个很好的解决方案吗?如上所述的问题中提到的第二种方法是在requestAnimationFrame内调用setInterval。对我来说似乎有点费解,但它可能是最好的选择吗?

还是有更好的选择来实现这个目标吗?

3 个答案:

答案 0 :(得分:14)

Yoshi的答案可能是解决这个问题的最佳代码。但我仍然认为这个答案是正确的,因为经过一些研究后我基本上发现我的问题无效。 requestAnimationFrame实际上是为了保持帧速率尽可能高,并且优化了动画要保持一致和平滑的场景。

值得注意的是,你不需要requestAnimationFrame来获得优化(即使rAF被吹捧为一个出色的性能助推器),因为浏览器仍然优化<canvas>的常规绘制。例如,当标签没有聚焦时,Chrome for one会停止绘制其画布。

所以我的结论是这个问题无效。希望这有助于任何想要与我相似的人。

答案 1 :(得分: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/

答案 2 :(得分:5)

你可以做什么,虽然我真的不知道这是否真的更好:

  • 使用requestAnimationFrame
  • 渲染到不可见的上下文
  • 使用固定的fps
  • 更新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>

演示:http://jsfiddle.net/54vWN/