Last active
March 27, 2022 03:02
-
-
Save iangilman/5824967 to your computer and use it in GitHub Desktop.
An improved version of Matt Snider's requestAnimationFrame polyfill.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Modified from http://mattsnider.com/cross-browser-and-legacy-supported-requestframeanimation/ | |
// LICENSE: MIT: http://mattsnider.com/projects/license/ | |
(function(w) { | |
"use strict"; | |
// most browsers have an implementation | |
w.requestAnimationFrame = w.requestAnimationFrame || | |
w.mozRequestAnimationFrame || w.webkitRequestAnimationFrame || | |
w.msRequestAnimationFrame; | |
w.cancelAnimationFrame = w.cancelAnimationFrame || | |
w.mozCancelAnimationFrame || w.webkitCancelAnimationFrame || | |
w.msCancelAnimationFrame; | |
// polyfill, when necessary | |
if (!w.requestAnimationFrame) { | |
var aAnimQueue = [], | |
aProcessing = [], | |
iRequestId = 0, | |
iIntervalId; | |
// create a mock requestAnimationFrame function | |
w.requestAnimationFrame = function(callback) { | |
aAnimQueue.push([++iRequestId, callback]); | |
if (!iIntervalId) { | |
iIntervalId = setInterval(function() { | |
if (aAnimQueue.length) { | |
var time = +new Date(); | |
// Process all of the currently outstanding frame | |
// requests, but none that get added during the | |
// processing. | |
// Swap the arrays so we don't have to create a new | |
// array every frame. | |
var temp = aProcessing; | |
aProcessing = aAnimQueue; | |
aAnimQueue = temp; | |
while (aProcessing.length) { | |
aProcessing.shift()[1](time); | |
} | |
} else { | |
// don't continue the interval, if unnecessary | |
clearInterval(iIntervalId); | |
iIntervalId = undefined; | |
} | |
}, 1000 / 50); // estimating support for 50 frames per second | |
} | |
return iRequestId; | |
}; | |
// create a mock cancelAnimationFrame function | |
w.cancelAnimationFrame = function(requestId) { | |
// find the request ID and remove it | |
var i, j; | |
for (i = 0, j = aAnimQueue.length; i < j; i += 1) { | |
if (aAnimQueue[i][0] === requestId) { | |
aAnimQueue.splice(i, 1); | |
return; | |
} | |
} | |
// If it's not in the queue, it may be in the set we're currently | |
// processing (if cancelAnimationFrame is called from within a | |
// requestAnimationFrame callback). | |
for (i = 0, j = aProcessing.length; i < j; i += 1) { | |
if (aProcessing[i][0] === requestId) { | |
aProcessing.splice(i, 1); | |
return; | |
} | |
} | |
}; | |
} | |
})(window); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
With the original, if you call requestAnimationFrame more than once between frames (as might happen in a complex animating environment), the polyfill queues up callbacks and then fires one per frame. This is not how the built-in requestAnimationFrame works; instead it fires every callback that's queued by the time the frame starts. This JSBin illustrates:
http://jsbin.com/iwasil/2
This Gist behaves the way the built-in requestAnimationFrame does.