requestAnimationFrame at beginning or end of function?

这一生的挚爱 提交于 2020-01-01 08:38:31

问题


If I have a loop using requestAnimationFrame like this:

function render() {
    // Rendering code

    requestAnimationFrame(render);
}

Will there be any difference if I put the requestAnimationFrame in the beginning of the function, like this:

function render() {
    requestAnimationFrame(render);

    // Rendering code
}

I haven't noticed any difference, but I have seen both implementations, is one of them better in any way, or are they the same?

Edit: One thing I have thought about is, if I put it in the beginning, and the render code takes quite long time to run, say 10ms, wouldn't putting it in the end make the frame rate drop with 10ms?


回答1:


requestAnimationFrame does always call its callback asynchronously, so as long as your rendering code is synchronous and does not throw exceptions it doesn't make any difference.

It's essentially a style choice, choose yourself which approach is cleaner. Putting it at the top may emphasise that render is scheduling itself, and does so even in the presence of errors in the rendering. Putting it in the bottom allows to conditionally break out of the rendering loop (e.g. when you want to pause your game).




回答2:


It likely won't make a diference. The requestAnimationFrame method is asynchronous, so either way, your render function will work as expected. But... there's a catch when it comes to halting. Say you have the following code:

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

In order to stop the next render, a call to the cancelAnimationFrame method is needed, like so:

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

Otherwise, the render method will just run indefinitely. Alternatively, you could do:

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

As for frame dropping, you could look at requestAnimationFrame as being on a fixed schedule (at 60 frames-per-second, it would be approximately 16ms intervals). If your code takes longer than that, the browser will begin to drop frames. Look at Patrick Roberts's answer for instructions on how to take charge of your frames, and use that for more consistent rendering.

I hope that helps!




回答3:


To answer your question, those two functions will make a difference in the amount of time the asynchronous callback takes to occur only if your rendering code is longer than the animation frame speed (typically around 16 - 33ms depending on browser implementation). However, if you were using this API as intended, even that shouldn't make a difference.

Note that you are opting out of using the optional parameter passed from requestAnimationFrame -- the timestamp.

Make sure to calculate your deltas if you have any delta-time-dependent animations to render. Typically you multiply an animation "velocity" with the timestamp delta (current timestamp minus previous timestamp) in order to get an effective distance an object should travel across the screen. Its effect is particularly noticeable when your rendering code does not consistently take the same amount of time to execute each frame.

Demo

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);
div {
  position: absolute;
  left: 20px;
  width: 10px;
  height: 10px;
}

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

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

Notice how the blue square appears to maintain a more consistent velocity overall. That is the intention.




回答4:


The MDN description states that:

The window.requestAnimationFrame() method tells the browser that you wish to perform an animation and requests that the browser call a specified function to update an animation before the next repaint.

When that repaint occurs is largely up to the browser. There shouldn't be any difference in behavior unless your JS is still running when the repaint would have occurred.

The WhatWG spec does not mention waiting for the JS call stack to clear or anything of the sort, although an exceptionally long-running function will block the UI thread and therefore should prevent animation frames from being called.



来源:https://stackoverflow.com/questions/38229386/requestanimationframe-at-beginning-or-end-of-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!