requestAnimationFrame在函数的开头或结尾?

sup*_*per 12 javascript requestanimationframe

如果我有一个使用requestAnimationFrame的循环,如下所示:

function render() {
    // Rendering code

    requestAnimationFrame(render);
}
Run Code Online (Sandbox Code Playgroud)

如果我把requestAnimationFrame函数放在函数的开头,会有什么区别,如下所示:

function render() {
    requestAnimationFrame(render);

    // Rendering code
}
Run Code Online (Sandbox Code Playgroud)

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

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

Ber*_*rgi 11

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

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

  • @JanDvorak 真的吗?Afaik,`requestAnimationFrame` 尝试在帧之间保持稳定的间隔,无论何时调用。如果渲染确实需要更长的时间,则将立即安排下一个渲染。 (2认同)

m-a*_*n-o 5

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

function render() {
    requestAnimationFrame(render);
    // Rendering code
}
Run Code Online (Sandbox Code Playgroud)

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

function render() {
    requestAnimationFrame(render);
    // Rendering code
    if (noLongerInterested) {
        cancelAnimationFrame();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

function render() {
    // Rendering code
    if (stillInterested) {
        requestAnimationFrame(render);
    }
}
Run Code Online (Sandbox Code Playgroud)

至于丢帧,您可以将其requestAnimationFrame视为固定计划(每秒 60 帧,大约是 16 毫秒的间隔)。如果您的代码需要更长的时间,浏览器将开始丢帧。查看帕特里克罗伯茨的回答,了解如何管理你的帧,并使用它来实现更一致的渲染。

我希望这有帮助!


Pat*_*rts 5

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

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

如果您有任何依赖于时间增量的动画要渲染,请务必计算增量。通常,您将动画“速度”与timestamp增量(当前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);
Run Code Online (Sandbox Code Playgroud)
div {
  position: absolute;
  left: 20px;
  width: 10px;
  height: 10px;
}

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

#timed {
  background-color: #00F;
  top: 50px;
}
Run Code Online (Sandbox Code Playgroud)
<div id="untimed"></div>
<div id="timed"></div>
Run Code Online (Sandbox Code Playgroud)

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