We can use requestAnimationFrame
in order to paint a frame in our browser. However, what happens if we have multiple animations on the same page?
If for example we have 3 custom marquees and some 2 fading effects, since each effect has it's own requestAnimationFrame
doesn't it mean that we are actually painting the browser 5 times each frame request?
Moreover, if I limit my text-scrollers FPS to be at 30FPS but my fading effects are running with 45FPS doesn't it mean that in a span of 1 second we are running in total 3*30 + 2*45 = 180 frame paintings?
Would it be better (considering the fact that I limit all of my animation on the page to the same FPS rate) to let 1 requestAnimationFrame
draw all of my animations? As a result I would end up with merely 30-60 frame painting per second (depends on the FPS limit)?
I'm simply trying to think of ways that help reduce the CPU usage as much as possible.
It's fairly easy to combine multiple effects into a single requestAnimationFrame
You use an array of javascript objects which define the timing of each of your effects:
var timers=[];
timers.push({delay:50,nextFireTime:0,doFunction:doEffect1,counter:0});
timers.push({delay:500,nextFireTime:0,doFunction:doEffect2,counter:0});
timers.push({delay:5000,nextFireTime:0,doFunction:doEffect3,counter:0});
You can use one requestAnimationFrame loop that iterates through the loop and triggers each effect based on a nextFireTime
function timerLoop(currentTime){
// request another loop
requestAnimationFrame(timerLoop);
// iterate through each timer
for(var i=0;i<timers.length;i++){
// if this timer has reached its
// next scheduled trigger time...
if(currentTime>timers[i].nextFireTime){
var t=timers[i];
// ...then do this effect
t.doFunction(t,i);
// and reset the timer to fire again in the future
t.nextFireTime=currentTime+t.delay;
}
}
}
Here's example code an a Demo:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
var timers=[];
timers.push({delay:50,nextFireTime:0,doFunction:doTimers,counter:0});
timers.push({delay:500,nextFireTime:0,doFunction:doTimers,counter:0});
timers.push({delay:5000,nextFireTime:0,doFunction:doTimers,counter:0});
//
requestAnimationFrame(timerLoop);
//
function timerLoop(currentTime){
// request another loop
requestAnimationFrame(timerLoop);
// iterate through each timer
for(var i=0;i<timers.length;i++){
// if this timer has reached its
// next scheduled trigger time...
if(currentTime>timers[i].nextFireTime){
var t=timers[i];
// ...then do this effect
t.doFunction(t,i);
// and reset the timer to fire again in the future
t.nextFireTime=currentTime+t.delay;
}
}
}
//
function doTimers(t,i){
// this demo just calls this one effect function
// but you would call separate effect functions
// for your marquis & fades.
ctx.clearRect(0,100+i*20-20,cw,20);
ctx.fillText('Timer#'+i+' with '+t.delay+'ms delay has fired '+(++t.counter)+' times.',20,100+20*i);
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>
requestAnimationFrame
reffers to an internal browser mechanism of rendering a frame of an entire webpage. When you say requestAnimationFrame
you are not telling it to create something for you, you are asking for it to tell you when a next frame is being rendered so you can hook up your action to it.
So you can use requestAnimationFrame
as many times as you want and the browser will sync your actions automatically by running them whenever a frame (which you requested) will be available.
Of course, performancevise it is better to avoid multiple per frame function calls (as they need to stack and execute one after another inside each frame) but from the synchronization point of view its quite irrelevant. Also, there is no way you will be multipling any frames or paints.