Skip to content

Instantly share code, notes, and snippets.

@louisremi
Created July 29, 2011 17:34
Show Gist options
  • Save louisremi/1114293 to your computer and use it in GitHub Desktop.
Save louisremi/1114293 to your computer and use it in GitHub Desktop.
Animation loop with requestAnimationFrame
function animLoop( render, element ) {
var running, lastFrame = +new Date;
function loop( now ) {
// stop the loop if render returned false
if ( running !== false ) {
requestAnimationFrame( loop, element );
var deltaT = now - lastFrame;
// do not render frame when deltaT is too high
if ( deltaT < 160 ) {
running = render( deltaT );
}
lastFrame = now;
}
}
loop( lastFrame );
}
function animLoop( render, element ) {
var running, lastFrame = +new Date;
function loop( now ) {
// stop the loop if render returned false
if ( running !== false ) {
requestAnimationFrame( loop, element );
running = render( now - lastFrame );
lastFrame = now;
}
}
loop( lastFrame );
}
// Usage
animLoop(function( deltaT ) {
elem.style.left = ( left += 10 * deltaT / 16 ) + "px";
if ( left > 400 ) {
return false;
}
// optional 2nd arg: elem containing the animation
}, animWrapper );
// Cross browser, backward compatible solution
(function( window, Date ) {
// feature testing
var raf = window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame ||
window.oRequestAnimationFrame;
window.animLoop = function( render, element ) {
var running, lastFrame = +new Date;
function loop( now ) {
if ( running !== false ) {
raf ?
raf( loop, element ) :
// fallback to setTimeout
setTimeout( loop, 16 );
// Make sure to use a valid time, since:
// - Chrome 10 doesn't return it at all
// - setTimeout returns the actual timeout
now = now && now > 1E4 ? now : +new Date;
var deltaT = now - lastFrame;
// do not render frame when deltaT is too high
if ( deltaT < 160 ) {
running = render( deltaT, now );
}
lastFrame = now;
}
}
loop();
};
})( window, Date );
// Usage
animLoop(function( deltaT, now ) {
// rendering code goes here
// return false; will stop the loop
...
// optional 2nd arg: elem containing the animation
}, animWrapper );
var elem = document.getElementById("animatedElem"),
left = 0,
lastFrame = +new Date,
timer;
// Move the element on the right at ~600px/s
timer = setInterval(function() {
var now = +new Date,
deltaT = now - lastFrame;
elem.style.left = ( left += 10 * deltaT / 16 ) + "px";
lastFrame = now;
// clear the timer at 400px to stop the animation
if ( left > 400 ) {
clearInterval( timer );
}
}, 16);
@louisremi
Copy link
Author

I make it a principle of not including un-prefixed APIs, as long as it isn't available in any browser.
requestAnimationFrame probably won't change much, but there's no point in adding prefixes if shims are written with assumptions that the final version of the API will behave exactly like the drafts.

@ptmrt
Copy link

ptmrt commented Sep 3, 2011

Thanks for the code snippets.
I think there's a bug in animLoopX.js.
if deltaT is too big, lastFrame never gets updated, so the loop never calls render anymore!

@louisremi
Copy link
Author

You're absolutely right, I updated it.
Thanks

@darius
Copy link

darius commented May 13, 2013

Would this do the same thing? It's simpler:

// Periodically call render(timeInterval) via requestAnimationFrame on
// element until it returns false. (But call render only when the
// interval is < 160ms; otherwise wait and try again, hoping for a
// reasonable interval.)
function animLoop(render, element) {
    var then = Date.now();
    function loop(now) {
        var interval = now - then;
        if (160 <= interval || render(interval) !== false)
            requestAnimationFrame(loop, element);
        then = now;
    }
    loop(then);
}

(Do any browsers or specifications use this element parameter?)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment