// http://paulirish.com/2011/requestanimationframe-for-smart-animating/ | |
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating | |
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel | |
// MIT license | |
(function() { | |
var lastTime = 0; | |
var vendors = ['ms', 'moz', 'webkit', 'o']; | |
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { | |
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; | |
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] | |
|| window[vendors[x]+'CancelRequestAnimationFrame']; | |
} | |
if (!window.requestAnimationFrame) | |
window.requestAnimationFrame = function(callback, element) { | |
var currTime = new Date().getTime(); | |
var timeToCall = Math.max(0, 16 - (currTime - lastTime)); | |
var id = window.setTimeout(function() { callback(currTime + timeToCall); }, | |
timeToCall); | |
lastTime = currTime + timeToCall; | |
return id; | |
}; | |
if (!window.cancelAnimationFrame) | |
window.cancelAnimationFrame = function(id) { | |
clearTimeout(id); | |
}; | |
}()); |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
Current Chrome (16) supports webkitRequestAnimationFrame but not webkitCancelAnimationFrame (or webkitCancelRequestAnimationFrame). This breaks the polyfill because it starts the animation loop with requestAnimationFrame, but there's no way to stop it. I've patched the polyfill to return the setTimeout in this case Not sure how to send a pull request for a gist... |
This comment has been minimized.
This comment has been minimized.
It should be webkitCancelRequestAnimationFrame exists in current Chrome (16.0.912.75) |
This comment has been minimized.
This comment has been minimized.
lol dyslexia. thx also. Fixed. fwiw, the new name is present in canary. |
This comment has been minimized.
This comment has been minimized.
I ran into an issue with Firefox 10, where |
This comment has been minimized.
This comment has been minimized.
Why cAF is not at all in FF10 is a complete mystery to me. Seems no tickets at all filed about it either... |
This comment has been minimized.
This comment has been minimized.
couldn't you simulate cAF by wrapping requestAnimationFrame in a function grabs the ID and passes in a wrapped function that only calls its inner function if cancelAnimationFrame has not been called with a particular ID? (which you can check by having your fake cancelAnimation set the passed in ID on a private/closure variable object to true, and then checking that key in the wrap function.) |
This comment has been minimized.
This comment has been minimized.
I tried this out on an iOS Phonegap project, and it runs at a really terrible frame rate on my iPhone 4. I switched back to using a simple setTimeout and am getting close to 60fps. The timeToCall calculation is not good on iOS. |
This comment has been minimized.
This comment has been minimized.
I think the timeToCall calculation is mostly useless anyway. It falls apart as soon as you call RAF twice in a row. And as mentioned by the commenter above adds a bunch of overhead. |
This comment has been minimized.
This comment has been minimized.
@paulirish [edit: whoops, was looking at a previous version of the gist. Removing some of the criticism here.] To handle @taybenlor’s concern about calling RAF multiple times in a row, we can do something like: var targetTime = 0;
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback) {
var currentTime = +new Date;
targetTime = Math.max(targetTime + 16, currentTime);
var timeoutCb = function() { callback(+new Date); }
return window.setTimeout(timeoutCb, targetTime - currentTime);
}; This also clarifies the code a bit (the previous variable names were a bit confusing), and makes sure that the timestamp passed to the callback is correct, because it is determined (by getting |
This comment has been minimized.
This comment has been minimized.
@jrus I think you misunderstand what is going on The time passed into the callback is |
This comment has been minimized.
This comment has been minimized.
@taybenlor yeah, but the semantics of requestAnimationFrame (as far as I can understand from reading the spec; it’s not entirely clear/explicit what time should be passed to the callback) should send the time when it’s actually called into the callback. [Also, I edited my previous comment a bit: was looking at the previous version of the gist when I first wrote it.] |
This comment has been minimized.
This comment has been minimized.
Here’s a cute CoffeeScript port of the whole thing do ->
w = window
for vendor in ['ms', 'moz', 'webkit', 'o']
break if w.requestAnimationFrame
w.requestAnimationFrame = w["#{vendor}RequestAnimationFrame"]
w.cancelAnimationFrame = (w["#{vendor}CancelAnimationFrame"] or
w["#{vendor}CancelRequestAnimationFrame"])
targetTime = 0
w.requestAnimationFrame or= (callback) ->
targetTime = Math.max targetTime + 16, currentTime = +new Date
w.setTimeout (-> callback +new Date), targetTime - currentTime
w.cancelAnimationFrame or= (id) -> clearTimeout id |
This comment has been minimized.
This comment has been minimized.
@Breton, @GEDDesign this version should I think do cAF properly in browsers that only implement rAF, assuming they actually return a unique ID for each call to rAF: do ->
w = window
for vendor in ['ms', 'moz', 'webkit', 'o']
break if w.requestAnimationFrame
w.requestAnimationFrame = w["#{vendor}RequestAnimationFrame"]
w.cancelAnimationFrame = (w["#{vendor}CancelAnimationFrame"] or
w["#{vendor}CancelRequestAnimationFrame"])
# deal with the case where rAF is built in but cAF is not.
if w.requestAnimationFrame
return if w.cancelAnimationFrame
browserRaf = w.requestAnimationFrame
canceled = {}
w.requestAnimationFrame = (callback) ->
id = browserRaf (time) ->
if id of canceled then delete canceled[id]
else callback time
w.cancelAnimationFrame = (id) -> canceled[id] = true
# handle legacy browsers which don’t implement rAF
else
targetTime = 0
w.requestAnimationFrame = (callback) ->
targetTime = Math.max targetTime + 16, currentTime = +new Date
w.setTimeout (-> callback +new Date), targetTime - currentTime
w.cancelAnimationFrame = (id) -> clearTimeout id |
This comment has been minimized.
This comment has been minimized.
I created a script somewhat based on this gist. |
This comment has been minimized.
This comment has been minimized.
Suggestion: var id = window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id; to lastTime = currTime + timeToCall;
return window.setTimeout(function() { callback(currTime + timeToCall); }, timeToCall); |
This comment has been minimized.
This comment has been minimized.
Just for fun... https://gist.github.com/1058681 |
This comment has been minimized.
This comment has been minimized.
Hello! There is my version of the polyfill It has a lot of size and performance optimizations |
This comment has been minimized.
This comment has been minimized.
Just some minor tweaks in case anyone is interested: implemented the single var pattern and LeoDutra's suggestion. (function() {
var lastTime = 0,
vendors = ['ms', 'moz', 'webkit', 'o'],
x,
length,
currTime,
timeToCall;
for(x = 0, length = vendors.length; x < length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
currTime = new Date().getTime();
timeToCall = Math.max(0, 16 - (currTime - lastTime));
lastTime = currTime + timeToCall;
return window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}()); |
This comment has been minimized.
This comment has been minimized.
Just downloaded the code, really easy to use and effective. |
This comment has been minimized.
This comment has been minimized.
Would it make sense to augment this shim so that the setTimeout fallback uses |
This comment has been minimized.
This comment has been minimized.
Maybe it's horribly overkill, but if you can multiple animations at the same time. It could pay of to use a list and avoid multiple setTimeout's, also making sure the requests runs at the same time. Although, it's probably overkill for normal usage... |
This comment has been minimized.
This comment has been minimized.
I've been testing this version out and for some reason i can't cancel the animation request in webkit browsers. It does cancel in Firefox though. Has anyone else run into this? |
This comment has been minimized.
This comment has been minimized.
Note that the latest browsers (confirmed in Chrome, Firefox and IE10) have moved away from Date-based timestamps on requestAnimationFrame. They now all use the new precision API. If you are having issues during the first 1 or 2 frames, and you are substracting Date.now or something similar, that's the reason. |
This comment has been minimized.
This comment has been minimized.
What is the licensing for this code? Is it reusable for commercial software? |
This comment has been minimized.
This comment has been minimized.
The rAF / cAF shim above for browsers that don't support it (read: IE9) was completely broken for me. In my app I call cAF if rAF is called before a previous callback completes, otherwise some serious lag happens. With the shim it was basically adding 16 ms for each callback so if a whole bunch of events were fired off, the actual final non-cancelled callback might not happen until many seconds later. Hence, I subtracted the 16 ms for each cAF. This works much better in IE9; the part I changed is as follows # handle legacy browsers which don't implement rAF
targetTime = 0
w.requestAnimationFrame = (callback) ->
targetTime = Math.max targetTime + 16, currentTime = +new Date
w.setTimeout (-> callback +new Date), targetTime - currentTime
w.cancelAnimationFrame = (id) ->
targetTime -= 16
clearTimeout id |
This comment has been minimized.
This comment has been minimized.
You can remove the o prefix, it won't ever be implemented. |
This comment has been minimized.
This comment has been minimized.
@mzgol How do you know that? |
This comment has been minimized.
This comment has been minimized.
I know I'm super late to the game here, but I actually needed to adjust this polyfill because I am making use of the time argument -- which mean I not only need to fill the method, I also need to overwrite any Here's my go (note: I didn't rewrite the cancelAnimationFrame polyfill, so I didn't include it here) var lastTime = 0,
vendors = ['ms', 'moz', 'webkit', 'o'],
polyfill = function(callback, element) {
var currTime = new Date().getTime(),
timeToCall = Math.max(0, 16 - (currTime - lastTime)),
id = window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
for(var x = 0, xx = vendors.length; x < xx && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
|| window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame) {
window.requestAnimationFrame = polyfill;
} else {
window.requestAnimationFrame(function() {
var isTime = false,
args = Array.prototype.slice.apply(arguments);
for (var i = 0, ii = args.length; i < ii; i++) {
if (typeof args === 'number') {
isTime = true;
break;
}
}
if (!isTime) {
window.requestAnimationFrame = polyfill;
}
});
} EDIT: Oh, and @yckart - because opera's moved to webkit. Hard to believe they wouldn't just use -webkit- instead of -o- |
This comment has been minimized.
This comment has been minimized.
webkitRequestAnimationFrame is broken in iOS if you try and use it on additional pages, you can reenable it by focusing on the url bar then back to the page. (tested on ios6.1 iPad3) |
This comment has been minimized.
This comment has been minimized.
A simpler fix for the problem mizzao pointed out: update lastTime after the timeout instead of immediately, so you never have to undo it. Also fixed to make sure request and cancelAnimationFrame are defined consistently, plus gratuitous style changes.
|
This comment has been minimized.
This comment has been minimized.
A couple people have offered versions that collect multiple callbacks into a list all called for the same frame -- my code should handle the same usage without any extra fuss, assuming multiple setTimeouts with the same target time will get called without too much time in between. (I wonder if they will.) |
This comment has been minimized.
This comment has been minimized.
Added cancellation support to @darius's mod (function(window) {
'use strict';
var lastTime = 0,
vendors = ['moz', 'webkit', 'o', 'ms'],
x;
// Remove vendor prefixing if prefixed and break early if not
for (x = 0; x < vendors.length && !window.requestAnimationFrame; x += 1) {
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame']
|| window[vendors[x] + 'CancelRequestAnimationFrame'];
}
// Check if full standard supported
if (!window.cancelAnimationFrame) {
// Check if standard partially supported
if (!window.requestAnimationFrame) {
// No support, emulate standard
window.requestAnimationFrame = function(callback) {
var now = new Date().getTime(),
nextTime = Math.max(lastTime + 16, now);
return window.setTimeout(function() { callback(lastTime = nextTime); }, nextTime - now);
};
window.cancelAnimationFrame = window.clearTimeout;
} else {
// Emulate cancel for browsers that don't support it
vendors = window.requestAnimationFrame;
lastTime = {};
window.requestAnimationFrame = function(callback) {
var id = x; // Generate the id (x is initialized in the for loop above)
x += 1;
lastTime[id] = callback;
// Call the vendors requestAnimationFrame implementation
vendors(function(timestamp) {
if (lastTime.hasOwnProperty(id)) {
var error;
try {
lastTime[id](timestamp);
} catch (e) {
error = e;
} finally {
delete lastTime[id];
if (error) { throw error; } // re-throw the error if an error occurred
}
}
});
// return the id for cancellation capabilities
return id;
};
window.cancelAnimationFrame = function(id) {
delete lastTime[id];
};
}
}
}(this)); |
This comment has been minimized.
This comment has been minimized.
Just published an slightly modified version of this polyfill on bower and jamjs if you use those package managers: https://github.com/ngryman/raf.js. |
This comment has been minimized.
This comment has been minimized.
I went through all the comments and revisions. Of all the iterations posted (or not), is there a best version of the polyfill to use as of July 12th 2013? |
This comment has been minimized.
This comment has been minimized.
As @DanB-66 noted, requestAnimationFrame is completely borked in a variety of scenarios: iframes, page-to-page navigation, and, in our observation, in conjunction with infinitely running CSS animations. CanIuse.com mentions that requestAnimationFrame has bugs. Other notable mentions: The problem has not been fixed (thus far) in iOS7. IMO, this shim should detect iOS and simply not support it, for the time being. |
This comment has been minimized.
This comment has been minimized.
I think it's time we move this to a repo. :D I won't be able to maintain it so I'm looking for some responsible people. Also we need to address the iOS issues. I haven't researched them in-depth myself but we need to resolve them ASAP. @KrofDrakula (his iOS-fix fork) or @darius .. either of you interested? |
This comment has been minimized.
This comment has been minimized.
@paulirish I would be happy to maintain it; I'm about to release code at work (SproutSocial) that relies on a very similar polyfil) |
This comment has been minimized.
This comment has been minimized.
very nice, thanks for sharing i made two little modifications, instead of checking for the method to be falsy like this: !window.requestAnimationFrame i check for it to be explicitly undefined window.requestAnimationFrame === undefined and also when declaring vars i do it in a single var statement: var lastTime = 0,vendors = ['ms', 'moz', 'webkit', 'o'],x; |
This comment has been minimized.
This comment has been minimized.
@paulirish, I just made a repo at https://github.com/darius/requestAnimationFrame. It has KrofDrakula's test merged into my version above, though I haven't tested that on an iOS device yet. |
This comment has been minimized.
This comment has been minimized.
just wrote an even better shim https://github.com/kof/animation-frame |
This comment has been minimized.
This comment has been minimized.
var now = Date.now || function() {
return +new Date
}
For example: var _KEY_AnimationFrame = 'AnimationFrame',
_KEY_requestAnimationFrame = 'request' + _KEY_AnimationFrame;
window[_KEY_requestAnimationFrame] = function(){/*............*/}
It is my fork: https://gist.github.com/Gaubee/6991570 |
This comment has been minimized.
This comment has been minimized.
FWIW, -o-requestAnimationFrame and -ms-requestAnimationFrame have never been used. Both browsers (IE 10+ and Opera 15+) use the unprefixed version. Firefox supports the unprefixed version since FF23, which was released last August (we are now 3 versions further).
|
This comment has been minimized.
This comment has been minimized.
I did cleaned up the original a little bit: // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
// MIT license
(function (window, rAF, cAF) {
var lastTime = 0, vendors = ['ms', 'moz', 'webkit', 'o'], x;
for (x = 0; x < vendors.length && !window[rAF]; ++x) {
window[rAF] = window[vendors[x] + 'RequestAnimationFrame'];
window[cAF] = window[vendors[x] + 'CancelAnimationFrame']
|| window[vendors[x] + 'CancelRequestAnimationFrame'];
}
if (!window[rAF]) {
window[rAF] = function (callback) {
var currTime = new Date().getTime(),
timeToCall = Math.max(0, 16 - (currTime - lastTime)),
id = window.setTimeout(function () { callback(currTime + timeToCall); }, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
}
if (!window[cAF]) {
window[cAF] = function (id) {
window.clearTimeout(id);
};
}
}(this, 'requestAnimationFrame', 'cancelAnimationFrame')); which compresses to 423 bytes:
|
This comment has been minimized.
This comment has been minimized.
@paulirish - Is this being hosted somewhere new now? There are links to various repos, but I couldn't tell which one of them is "officially sanctioned". |
This comment has been minimized.
This comment has been minimized.
@knod - As far as I can tell, the requestAnimationFrame polyfill-repo from @darius is the most up-to-date one. If you want sub-millisecond precision, you can try adding the performance.now polyfill from @timhall after @darius's one. @timhall also uses the more compatible shorthand notation instead of Date.now().
|
This comment has been minimized.
This comment has been minimized.
Currently, I found these :
I can't tell which code is included in another one or not, and which is better... Maybe @kof has the answer ^^ |
This comment has been minimized.
This comment has been minimized.
I have created my library after reading all of this shims/polyfills. It includes all improvements and some additional features, like custom frame rate, feature detected raf (required in cases where raf is implemented but just doesn't work), using raf from the iframe, increased performance for multiple parallel animations when using non native version by grouping callback calls and using just one timer ... |
This comment has been minimized.
This comment has been minimized.
Thanks a lot for your quick answer, now I can move forward in my project with serenity ;) |
This comment has been minimized.
This comment has been minimized.
I think that the approach of using new timer for each callback can cause unexpected behaviour. You can overcome this using callback queue and single timer. (function (global) {
var callbacksQueue = [];
global.setInterval(function () {
for (var i = 0; i < callbacksQueue.length; i++) {
if (callbacksQueue[i] !== false) {
callbacksQueue[i].call(null);
}
}
callbacksQueue = [];
}, 1000 / 60);
global.requestAnimationFrame = function (callback) {
return callbacksQueue.push(callback) - 1;
}
global.cancelAnimationFrame = function (id) {
callbacksQueue[id] = false;
}
}(window)); |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
If you use JSLint:
|
This comment has been minimized.
This comment has been minimized.
What is the window.requestAnimationFrame = function(callback, element) { It's unused, and according to MDN the function takes just one argument. |
This comment has been minimized.
This comment has been minimized.
To better match the timestamp passed to the var currTime = new Date().getTime() - window.performance.timing.navigationStart; Without this change my animations on IE9 were not correctly calculating compared to the browsers with native Note: |
This comment has been minimized.
This comment has been minimized.
@davidchambers - It was removed from the spec - https://bugs.webkit.org/show_bug.cgi?id=74232 |
This comment has been minimized.
This comment has been minimized.
ES2015 flavored + performance.now() {
const vendors = ['ms', 'moz', 'webkit', 'o']
const af = 'AnimationFrame'
let lastTime = 0
if('performance' in window == false)
window.performance = {}
if(!Date.now)
Date.now = () => new Date().getTime()
if ('now' in window.performance == false){
let nowOffset = Date.now()
if(performance.timing && performance.timing.navigationStart)
nowOffset = performance.timing.navigationStart
window.performance.now = () => Date.now() - nowOffset
}
for(let x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
const vendor = vendors[x]
window.requestAnimationFrame = window[`${vendor}Request${af}`]
window.cancelAnimationFrame = window[`${vendor}Cancel${af}`] || window[`${vendor}CancelRequest${af}`]
}
if(!window.requestAnimationFrame){
window.requestAnimationFrame = callback =>{
const currTime = Date.now()
const timeToCall = Math.max(0, 16 - (currTime - lastTime))
const id = window.setTimeout(() => callback(currTime + timeToCall), timeToCall)
lastTime = currTime + timeToCall
return id
}
}
if(!window.cancelAnimationFrame)
window.cancelAnimationFrame = id => clearTimeout(id)
} |
This comment has been minimized.
This comment has been minimized.
This was very helpful for me. Thanks! I'm using it in a generic animation library I wrote. I hope this is okay. https://github.com/imthenachoman/nAnimation |
This comment has been minimized.
This comment has been minimized.
@imthenachoman Same issue as you, what's the license for this? I'm planning to use this in a game engine I'm writing. |
This comment has been minimized.
This comment has been minimized.
@imthenachoman It says it's MIT license; you can probably do whatever you want. |
This comment has been minimized.
This comment has been minimized.
I believe it's best to use as few requestAnimationFrame instances as possible. I avoided frame rates and delays as they're better utilized outside of the polyfill, (vai the timestamp). Thanks to @darius for the timing tweaks, as well as everyone else on here. |
This comment has been minimized.
This comment has been minimized.
[20:22:43] bower download https://gist.githubusercontent.com/paulirish/1579671/raw/3d42a3a76ed09890434a07be2212f376959e616f/rAF.js Anyone else getting this:
|
This comment has been minimized.
This comment has been minimized.
Due to an unconfirmed bug in OldIE(maybe the poor performance), many tasks were delaying running. There are two intended bad Fibonacci demos.
|
This comment has been minimized.
This comment has been minimized.
In this article, David Geary implements a interesting version of polyfill. What do you think about it? |
This comment has been minimized.
This comment has been minimized.
I've written a solution that doesn't queue each callback separately but execute all scheduled for next frame simultaneously: https://github.com/milosdjakonovic/requestAnimationFrame-polyfill Second (third, fourth...) opinion and eyeball check would do good. |
This comment has been minimized.
This comment has been minimized.
In most situations I use miniraf |
This comment has been minimized.
This comment has been minimized.
Maybe I'm still missing something but for me the polyfill is not working right now and I am triong to figure out what is missing Uncaught SyntaxError: Missing initializer in const declaration |
This comment has been minimized.
This comment has been minimized.
Hai , i am new to angular2. i have a conflict in loading the angular2 webapp in safari browser. Actually where to use this code in the code so that i can get rid of this browser problem. is this also the solution for improper loading of c3 charts in certain browsers like safari and microsoft edge. please help me out and thanks in advance. |
This comment has been minimized.
This comment has been minimized.
Hi Vennapusaravali, More suitable for the Angular 4 repo, however if you checkout this JS file and add the script to angular-cli.jsons "scripts": [ ".../path/to/this/file/rAF.js" ] it should load correctly. More info can be found here. |
This comment has been minimized.
This comment has been minimized.
Hello, can someone help me with a question in my website? I don't know where is the issue list, or who I can ask. My website is not working the parallax.js (http://memphisnet.com.br/3.0/) Thanks advice |
This comment has been minimized.
This comment has been minimized.
Hi guys, Anyone know the answer to this question: https://stackoverflow.com/questions/52508261/do-we-need-to-clear-a-timeout-when-using-requestanimationframe-polyfill ? Would like to hear a professional opinion. Thanks |
This comment has been minimized.
This comment has been minimized.
I've been reading a lot of the comments but I am kinda bit confused now which code to be implemented. |
This comment has been minimized.
This comment has been minimized.
Hello, I just adapted Paul Irish's polyfill to work with high resolution timing automatically (when possible) and improved the performance a little bit. It can work with the following window.performance.now polyfill: https://gist.github.com/jalbam/cc805ac3cfe14004ecdf323159ecf40e Here it is: 'use strict';
// requestAnimationFrame polyfill by Erik Möller.
// Fixes from Paul Irish, Tino Zijdel, Andrew Mao, Klemen Slavic, Darius Bacon and Joan Alba Maldonado.
// Adapted from https://gist.github.com/paulirish/1579671 which derived from
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// Added high resolution timing. This window.performance.now() polyfill can be used: https://gist.github.com/jalbam/cc805ac3cfe14004ecdf323159ecf40e
// MIT license
// Gist: https://gist.github.com/jalbam/5fe05443270fa6d8136238ec72accbc0
(function() {
var vendors = ['webkit', 'moz', 'ms', 'o'], vp = null;
for (var x = 0; x < vendors.length && !window.requestAnimationFrame && !window.cancelAnimationFrame; x++)
{
vp = vendors[x];
window.requestAnimationFrame = window.requestAnimationFrame || window[vp + 'RequestAnimationFrame'];
window.cancelAnimationFrame = window.cancelAnimationFrame || window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame'];
}
if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) || !window.requestAnimationFrame || !window.cancelAnimationFrame) //iOS6 is buggy.
{
var lastTime = 0;
window.requestAnimationFrame = function(callback, element)
{
var now = window.performance.now();
var nextTime = Math.max(lastTime + 16, now);
return setTimeout(function() { callback(lastTime = nextTime); }, nextTime - now);
};
window.cancelAnimationFrame = clearTimeout;
}
}()); You can found it in this gist: https://gist.github.com/jalbam/5fe05443270fa6d8136238ec72accbc0 Any comments are welcome. Thank you very much. |
This comment has been minimized.
Not a bad shim. A few improvements could be made, though:
1.) use Date.now() instead of new Date().getTime(), avoids one object allocation
2.) Date.now() and setTimeout() operate in terms of integer milliseconds, but 60fps doesn't map to an integer number of milliseconds. This tries to map to 16ms/frame, which is 62.5 and will produce aliasing (meaning bad looking frames) every few seconds. See https://lists.webkit.org/pipermail/webkit-dev/2011-September/018095.html for a very wordy description of why. It is possible to do better, however, by tracking the error between the actual callback rate and the target. http://trac.webkit.org/browser/trunk/Source/WebCore/platform/graphics/chromium/cc/CCDelayBasedTimeSource.cpp?rev=104644#L101 describes in detail how to do this with some C++ code
3.) It's slightly better to set the timer before ticking rather than after - you don't have to worry about setTimeout() clamping if the callback takes >12ms to run and the precision errors are less of a concern. The easiest way to do this is to setTimeout() before invoking the callbacks assuming that somebody will call requestAnimationFrame() within the ticks, and then if they don't end up doing that call clearTimeout() after running the callbacks.