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