-
-
Save joelambert/1002116 to your computer and use it in GitHub Desktop.
Drop in replace functions for setTimeout() & setInterval() that | |
make use of requestAnimationFrame() for performance where available | |
http://www.joelambert.co.uk | |
Copyright 2011, Joe Lambert. | |
Free to use under the MIT license. | |
http://www.opensource.org/licenses/mit-license.php |
// requestAnimationFrame() shim by Paul Irish | |
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ | |
window.requestAnimFrame = (function() { | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
window.oRequestAnimationFrame || | |
window.msRequestAnimationFrame || | |
function(/* function */ callback, /* DOMElement */ element){ | |
window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); |
/** | |
* Behaves the same as setInterval except uses requestAnimationFrame() where possible for better performance | |
* @param {function} fn The callback function | |
* @param {int} delay The delay in milliseconds | |
*/ | |
window.requestInterval = function(fn, delay) { | |
if( !window.requestAnimationFrame && | |
!window.webkitRequestAnimationFrame && | |
!(window.mozRequestAnimationFrame && window.mozCancelRequestAnimationFrame) && // Firefox 5 ships without cancel support | |
!window.oRequestAnimationFrame && | |
!window.msRequestAnimationFrame) | |
return window.setInterval(fn, delay); | |
var start = new Date().getTime(), | |
handle = new Object(); | |
function loop() { | |
var current = new Date().getTime(), | |
delta = current - start; | |
if(delta >= delay) { | |
fn.call(); | |
start = new Date().getTime(); | |
} | |
handle.value = requestAnimFrame(loop); | |
}; | |
handle.value = requestAnimFrame(loop); | |
return handle; | |
} | |
/** | |
* Behaves the same as clearInterval except uses cancelRequestAnimationFrame() where possible for better performance | |
* @param {int|object} fn The callback function | |
*/ | |
window.clearRequestInterval = function(handle) { | |
window.cancelAnimationFrame ? window.cancelAnimationFrame(handle.value) : | |
window.webkitCancelAnimationFrame ? window.webkitCancelAnimationFrame(handle.value) : | |
window.webkitCancelRequestAnimationFrame ? window.webkitCancelRequestAnimationFrame(handle.value) : /* Support for legacy API */ | |
window.mozCancelRequestAnimationFrame ? window.mozCancelRequestAnimationFrame(handle.value) : | |
window.oCancelRequestAnimationFrame ? window.oCancelRequestAnimationFrame(handle.value) : | |
window.msCancelRequestAnimationFrame ? window.msCancelRequestAnimationFrame(handle.value) : | |
clearInterval(handle); | |
}; |
/** | |
* Behaves the same as setTimeout except uses requestAnimationFrame() where possible for better performance | |
* @param {function} fn The callback function | |
* @param {int} delay The delay in milliseconds | |
*/ | |
window.requestTimeout = function(fn, delay) { | |
if( !window.requestAnimationFrame && | |
!window.webkitRequestAnimationFrame && | |
!(window.mozRequestAnimationFrame && window.mozCancelRequestAnimationFrame) && // Firefox 5 ships without cancel support | |
!window.oRequestAnimationFrame && | |
!window.msRequestAnimationFrame) | |
return window.setTimeout(fn, delay); | |
var start = new Date().getTime(), | |
handle = new Object(); | |
function loop(){ | |
var current = new Date().getTime(), | |
delta = current - start; | |
delta >= delay ? fn.call() : handle.value = requestAnimFrame(loop); | |
}; | |
handle.value = requestAnimFrame(loop); | |
return handle; | |
}; | |
/** | |
* Behaves the same as clearTimeout except uses cancelRequestAnimationFrame() where possible for better performance | |
* @param {int|object} fn The callback function | |
*/ | |
window.clearRequestTimeout = function(handle) { | |
window.cancelAnimationFrame ? window.cancelAnimationFrame(handle.value) : | |
window.webkitCancelAnimationFrame ? window.webkitCancelAnimationFrame(handle.value) : | |
window.webkitCancelRequestAnimationFrame ? window.webkitCancelRequestAnimationFrame(handle.value) : /* Support for legacy API */ | |
window.mozCancelRequestAnimationFrame ? window.mozCancelRequestAnimationFrame(handle.value) : | |
window.oCancelRequestAnimationFrame ? window.oCancelRequestAnimationFrame(handle.value) : | |
window.msCancelRequestAnimationFrame ? window.msCancelRequestAnimationFrame(handle.value) : | |
clearTimeout(handle); | |
}; |
And I like for you to consider performance.now() would be a better decision than new Date().now();
here are two sources to help you consider this:
performance date now vs date gettime
https://stackoverflow.com/questions/12517359/performance-date-now-vs-date-gettime
performance now vs date now
https://stackoverflow.com/questions/30795525/performance-now-vs-date-now
It to me sound like what you have is correct but we are looking a performance and it seem from what I read, you can get better result with performance and timing with animation using the performance.now() so since we don't really need to know about the exact time and only for a precision of how long it been... So maybe like I said what you have already is better.
I think this is still useful in 2019, but we do not need all the checks for animationFrame functionality, unless you are supporting some seriously old browsers:
https://caniuse.com/#feat=requestanimationframe
i.e. lots of the above code can be stripped out.
I've noticed that when switching over from setInterval
to requestInterval
, my old delay (45 fps, don't judge me, 22.22 ms) was outputting around ~33 fps. Looking into it, this is because the specified delay isn't a multiple of the 60 fps, 16.66 ms delay. To fix this, I used the following:
requestInterval.js ln 23:
start = Date.now() - (delta % delay)
Date.now()
is a bit faster, and accounting for the multiple of my delay fixed the issue completely. Without this, I believe even when setting a target delay of 60 fps, 16.66 ms, the actual fps reached would be something like 30-45, since some frames would be skipped at random.
Sorry for bringing back an old thread, just leaving this here for anyone who needs it!
I did a fork and corrected that but when I thank about it, it is more simple to just pass that handle than to explicit pass the number value so maybe the real correction would be to remove that int| from "@param {int|object} fn The callback function"