public
Last active

Provides requestAnimationFrame in a cross browser way.

  • Download Gist
RequestAnimationFrame.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/**
* Provides requestAnimationFrame in a cross browser way.
* @author paulirish / http://paulirish.com/
*/
 
if ( !window.requestAnimationFrame ) {
 
window.requestAnimationFrame = ( function() {
 
return window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function( /* function FrameRequestCallback */ callback, /* DOMElement Element */ element ) {
 
window.setTimeout( callback, 1000 / 60 );
 
};
 
} )();
 
}

You can remove the window.requestAnimationFrame || bit since you will only get here when window.requestionAnimationFrame is not already "truthy".
:-)

hey i wrote that!

gregg stealing my glory. >:)

Now you know how it feels! (Google Axis ;D)

Now seriously, he didn't specify... he pasted the snippet on a issue:
https://github.com/mrdoob/three.js/issues/closed#issue/118

I'll change the credit ;)

Basically. Gregg Tavares had an uglier one there before and I rewrote it to what I just posted: http://paulirish.com/2011/requestanimationframe-for-smart-animating/

Then Gregg made it a proper polyfill with the full requestAnimationFrame method (and dropped it into webgl-utils). I don't totally agree with that as the spec might potentially change. Haven't walked over and talked to him about it yet. :)

Should it not be setInverval? I read that setTimeout comes with extra unwanted delays (http://nokarma.org/2010/02/02/javascript-game-development-the-game-loop/).

paul: I see I see! :)

louisstow: If you have the setTimeout at the befinning of the loop you shouldn't get delays.

surely there should be something to sync animations - so they all occur in the same event loop ?

window.requestAnimFrame = (function() {
  var fns = []
    , timeout;

  function run() {
    while(fns.length) fns.shift()()
    timeout = null
  }

  var self = this
  // shim layer with setTimeout fallback
  var onNextFrame = self.requestAnimationFrame       || 
                    self.webkitRequestAnimationFrame || 
                    self.mozRequestAnimationFrame    || 
                    self.oRequestAnimationFrame      || 
                    self.msRequestAnimationFrame     || 
                    function( callback ) {
                      self.setTimeout(callback, 1000 / 60);
                    };

  return function(fn) {
    fns.push(fn)
    timeout = timeout || onNextFrame(run)
  }

})();

How would you modify the latest jQuery with this shim and should I expect any performance improvements??

I believe this is the one line that sets up the animation timing
timerId = setInterval( fx.tick, fx.interval );

jquery 1.6.1 supported this:

if ( t() && jQuery.timers.push(t) && !timerId ) {
            // Use requestAnimationFrame instead of setInterval if available
            if ( requestAnimationFrame ) {
                timerId = 1;
                raf = function() {
                    // When timerId gets set to null at any point, this stops
                    if ( timerId ) {
                        requestAnimationFrame( raf );
                        fx.tick();
                    }
                };
                requestAnimationFrame( raf );
            } else {
                timerId = setInterval( fx.tick, fx.interval );
            }
        }

setTimeout is many times inaccurate, and so is setInterval. More importantly, however, setTimeout(1000/60) doesn't take into account where we are wrt to the next vsync (it always assume we have the full frame time ahead of us), so it needs to align to the "independent" intervals of an imaginary vsync signal.

var rAF = function(callback) {
  var interval = 1000 / 60;
  var now = (window.performance && window.performance.now) ?
            window.performance.now() :
            Date.now();
  // setTimeout can return early, make sure to target the next frame.
  if (this.lastTarget && now < this.lastTarget)
    now = this.lastTarget + 0.01; // Floating point errors may result in just too early.
  var delay = interval - now % interval;
  this.lastTarget = now + delay;
  setTimeout(callback, delay);
};

My animation is magically smooth now! ;)

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.