requestAnimationFrame在函数的开头或结尾?

时间:2016-07-06 16:47:23

标签: javascript requestanimationframe

如果我有一个使用requestAnimationFrame的循环:

function render() {
    // Rendering code

    requestAnimationFrame(render);
}

如果我将requestAnimationFrame放在函数的开头,会有什么不同,如下所示:

function render() {
    requestAnimationFrame(render);

    // Rendering code
}

我没有注意到任何差异,但我已经看到了两种实现方式,其中一种方式更好,或者它们是否相同?

编辑: 我想到的一件事是,如果我把它放在开头,并且渲染代码需要很长时间才能运行,比如说10ms,最终会不会使帧速率下降10ms?

4 个答案:

答案 0 :(得分:8)

requestAnimationFrame总是异步调用它的回调,所以只要你的渲染代码是同步的并且不抛出异常,它就没有任何区别。

它本质上是一种风格选择,选择自己哪种方法更清洁。将其置于顶部可能会强调render正在调度自身,即使在渲染中出现错误也会这样做。将其置于底部允许有条件地突破渲染循环(例如,当您想要暂停游戏时)。

答案 1 :(得分:3)

它可能不会产生差异。 requestAnimationFrame方法是异步的,因此无论哪种方式,渲染函数都将按预期工作。但是......当谈到停止时,有一个问题。假设您有以下代码:

function render() {
    requestAnimationFrame(render);
    // Rendering code
}

为了停止下一个渲染,需要调用cancelAnimationFrame方法,如下所示:

function render() {
    requestAnimationFrame(render);
    // Rendering code
    if (noLongerInterested) {
        cancelAnimationFrame();
    }
}

否则,render方法将无限期地运行。或者,您可以这样做:

function render() {
    // Rendering code
    if (stillInterested) {
        requestAnimationFrame(render);
    }
}

对于丢帧,您可以将requestAnimationFrame视为固定时间表(每秒60帧,间隔约为16毫秒)。如果您的代码需要更长的时间,浏览器将开始删除帧。请查看Patrick Roberts's answer,了解如何掌控您的相框,并将其用于更一致的渲染。

我希望有所帮助!

答案 2 :(得分:2)

要回答您的问题,只有当您的渲染代码长于动画帧速度(通常大约16-33ms,具体取决于浏览器实现)时,这两个函数才会对异步回调发生的时间产生影响。但是,如果您按预期使用此API,即使这样做也不会有所作为。

请注意,您选择不使用从requestAnimationFrame传递的可选参数 - timestamp

如果您要渲染任何与delta时间相关的动画,请务必计算您的增量。通常,您可以乘以动画"速度"使用timestamp delta(当前timestamp减去之前的timestamp),以获得对象应在屏幕上移动的有效距离。当渲染代码不能始终花费相同的时间来执行每一帧时,它的效果尤其明显。

演示



var untimed = 20;
var timed = 20;

function untimedRender() {
  var then = performance.now() + Math.random() * 100;

  while (performance.now() < then) {}
  
  // estimated velocity
  untimed += 50 / 30;
  
  document.querySelector('#untimed').style.left = Math.min(Math.floor(untimed), 200) + 'px';
  
  if (untimed < 200) {
    requestAnimationFrame(untimedRender);
  } else {
    last = performance.now();
    requestAnimationFrame(timedRender);
  }
}

var last;

function timedRender(timestamp) {
  var delta = timestamp - last;
  var then = timestamp + Math.random() * 100;
  
  last = timestamp;

  while (performance.now() < then) {}
  
  // calculated velocity
  timed += delta / 30;
  
  document.querySelector('#timed').style.left = Math.min(Math.floor(timed), 200) + 'px';
  
  if (timed < 200) {
    requestAnimationFrame(timedRender);
  }
}

requestAnimationFrame(untimedRender);
&#13;
div {
  position: absolute;
  left: 20px;
  width: 10px;
  height: 10px;
}

#untimed {
  background-color: #F00;
  top: 20px;
}

#timed {
  background-color: #00F;
  top: 50px;
}
&#13;
<div id="untimed"></div>
<div id="timed"></div>
&#13;
&#13;
&#13;

注意蓝色方块如何保持整体更加一致的速度。这是意图。

答案 3 :(得分:-1)

MDN description表示:

  

window.requestAnimationFrame()方法告诉浏览器您希望执行动画并请求浏览器调用指定的函数以在下次重绘之前更新动画。

当重绘时很大程度上取决于浏览器。除非你的JS在重绘时仍在运行,否则行为不应该有任何不同。

The WhatWG spec没有提到等待JS调用堆栈清除或任何排序,虽然一个特别长时间运行的函数将阻止UI线程,因此应该阻止动画帧被调用。