Multiple requestAnimationFrame performance
If I’m doing multiple animations, is it OK performance-wise to add multiple requestAnimationFrame
callbacks? F.ex:
function anim1() {
// animate element 1
}
function anim2() {
// animate element 2
}
function anim3() {
// animate element 3
}
requestAnimationFrame(anim1);
requestAnimationFrame(anim2);
requestAnimationFrame(anim3);
Or is it proven worse than using a single callback:
(function anim() {
requestAnimationFrame(anim);
anim1();
anim2();
anim3();
}());
I’m asking because I don’t really know what is going on behind the scenes, is requestAnimationFrame
queuing callbacks when you call it multiple times?
Solution 1:
I don't think any of these answers really explained what I was looking for: "do n calls to requestAnimationFrame" get debounced (i.e. dequeued 1 per frame) or all get invoked in the next frame.
When callbacks queued by requestAnimationFrame() begin to fire multiple callbacks in a single frame (mdn)
This suggests the latter, multiple callbacks can be invoked in the same frame.
I confirmed with the following test. A 60 hz refresh rate translates to a 17ms period. If it were the former, no 2 timestamps would be within 17ms of each other, but that was not the case.
let sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
let update = async timestamp => {
console.log('update called', timestamp)
await sleep(10);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
requestAnimationFrame(update);
Solution 2:
You should be using only one requestAnimationFrame
call as calls to requestAnimationFrame
do stack. The single callback version is thus more performant.
Solution 3:
Someone benchmarked this. Let's talk...
https://jsperf.com/single-raf-draw-calls-vs-multiple-raf-draw-calls
I looked at the performance comparison (you should too). You're welcome to disagree. These are drawing primitives on a canvas element.
function timeStamp() {
return window.performance && window.performance.now ? window.performance.now() : new Date().getTime();
}
function frame() {
drawCircle();
drawLines();
drawRect();
}
function render() {
if (timeStamp() >= (time || timeStamp())) {
time = timeStamp() + delayDraw;
frame();
}
requestAnimationFrame(render);
}
function render1() {
if (timeStamp() >= (time || timeStamp())) {
time = timeStamp() + delayDraw;
drawCircle();
}
requestAnimationFrame(render1);
}
function render2() {
if (timeStamp() >= (time || timeStamp())) {
time = timeStamp() + delayDraw;
drawRect();
}
requestAnimationFrame(render2);
}
function render3() {
if (timeStamp() >= (time || timeStamp())) {
time = timeStamp() + delayDraw;
drawLines();
}
requestAnimationFrame(render3);
}
I think this code is really benchmarking 7 calls to timestamp() vs 2 calls to timestamp(). Look at the difference between Chrome 46 and 47.
- Chrome 46: 12k/sec (one call) vs 12k/sec (3 calls)
- Chrome 47: 270k/sec (one call) vs 810k/sec (3 calls)
I think this is so well optimized that it doesn't make a difference. This is just measuring noise at this point.
My takeaway is this doesn't need to be hand-optimized for my application.
If you're worried about performance look at the difference between Chrome 59 (1.8m ops/sec) vs Chrome 71 (506k ops/sec).