#9381 closed bug (fixed)
animations halt when the browser is out of focus due to requestAnimationFrame
Reported by: | Owned by: | Timmy Willison | |
---|---|---|---|
Priority: | low | Milestone: | 1.6.3 |
Component: | effects | Version: | 1.6.1 |
Keywords: | needsreview | Cc: | |
Blocked by: | Blocking: |
Description
Hello,
I found a little nasty bug (:)) when using the fadeIn() and fadeOut() functions with setTimeout() (probably also a problem with setInterval()) and version 1.6.1 (and 1.6) in Chrome.
The problem is that all animations using fadeIn() and fadeOut() seem to be blocked when the browser window is minimized. When it is reopened the animations fires rapidly until all have been executed, and it resumes to work normally.
I have created an example on jsfiddle. http://jsfiddle.net/9RNzC/
Just open it, minimize the browser window, wait for 10 seconds (etc - it changes every 3 second), and reopen it again, and it will fire the animation effects rapidly, like they have been blocked while minimized.
It works fine with version 1.5.2 so something have probably changed between 1.5.2 and 1.6 in the animate function.
Hope it helps.
Best regards
Martin
Change History (35)
comment:1 Changed 12 years ago by
Component: | unfiled → effects |
---|---|
Owner: | set to gnarf |
Status: | new → assigned |
comment:2 Changed 12 years ago by
Just noticed that i forgot to change the out commented code using the show() and hide() functions, when i tried to simplify the code.
I have just updated it, if you want to see that it works when using the show() and hide() functions (remember to out comment the code and comment out the fadeIn() and fadeOut() calls above()).
It doesnt make a difference for the actual problem, just wanted to note it in case you were trying it out and wondered why it isnt working.
comment:3 Changed 12 years ago by
Owner: | gnarf deleted |
---|---|
Status: | assigned → open |
Yeah, sadly because of the way that requestAnimationFrame()
works (it does't give you any updates while the page isn't focused) this is exactly what will happen.
- unfocus tab
- You queue an animation....
- No animation frames are given, so you queue another animation, etc, etc
- focus tab
- Animation Frame is given, you finally get the second frame of the first animation
- First animation Completes, second starts
- The rest of your queue plays out.
To avoid: Don't use setTimeout() like that directly, instead use the callback of one of the animations to setTimeout for the next transition... Or you could use the queue itself...
Either of these solutions will work around the issue:
elem.fadeIn(1000, function() { setTimeout( nextCycle, 2000 ); }); // or.... elem.fadeIn(1000).delay(2000).queue(function( next ) { // tell the cycler to move on nextCycle(); // then call the next queue function next(); });
I don't see any way that we can avoid the "huge queue playback" of an animation like this that doesn't expect "stalling" time when the window isn't focused.
comment:5 Changed 12 years ago by
Keywords: | needsdocs added |
---|---|
Priority: | undecided → low |
gnarf's suggestion is really what should happen instead. I think we should document this behavior and leave it as is.
comment:6 Changed 12 years ago by
http://api.jquery.com/animate/ -- Added the following note to the animation functions:
Because of the nature of requestAnimationFrame(), you should never queue animations using a setInterval or setTimeout loop. In order to preserve CPU resources, browsers that support requestAnimationFrame will not update animations when the window/tab is not displayed. If you continue to queue animations via setInterval or setTimeout while animation is paused, all of the queued animations will begin playing when the window/tab regains focus. To avoid this potential problem, use the callback of your last animation in the loop to set a timeout to start the next animation.
comment:7 Changed 12 years ago by
Keywords: | needsdocs removed |
---|---|
Resolution: | → wontfix |
Status: | open → closed |
comment:8 Changed 12 years ago by
If it is possible for you somehow, it would be great if you could let some of all the major plugin makers know about this, because this will break a whole lot of plugins.
When making my plugin which just cycles between elements and fade them in and out, i looked at other plugins, and as far as i remember all of them will break because of this.
Why not go back to how it was before ? It worked fine, and how you would expect it to work. Now with then new version you have to do some kind of "hack" to make it play nice with jQuery.
Any way, great work with jQuery, it is really great. :)
comment:9 Changed 12 years ago by
Good way to work around this issue is to disable repeating animations using window.onfocus and window.onblur. By doing so animationts are not stacked together when the browser gets minimized or another tab is active. Example:
function windowActive() { focus = true; } function windowInactive() { focus = false; } window.onfocus = windowActive; window.onblur = windowInactive; var animation = function(){ if(focus){ //code } }
comment:10 follow-up: 16 Changed 12 years ago by
Adam: That is not a good solution because it will stop the animation even if the user is able to see the window. You could have 2 windows (or more) running side by side but without having focus.
Wasnt it one of the ideas behind jQuery in the first place that you shouldnt have to do "hacks" and write browser specific code to make it work ?
I dont want to mix my transition logic with my "autoplay" (cycle) logic as they are seperate things and dont belong together. The animation logic should also work when not set to automatically cycle between elements, so user can just click the element they want to go to.
The work arounds suggested earlier is not great. I much prefer it working like expected without having to do hacks to make jQuery play nice. It was working fine with older versions, so it should be possible.
comment:11 Changed 12 years ago by
currently to fix the problem i have just implemented a varaible (ready) containing the state.
So before i do an animation I set it to false, and at the end of the animation (using the callback) i set it to true.
In the autoplay function i just check the ready variable, and only call the animation method if true.
Then the animations doesnt stack up, as the autoplay stops calling when animations start to not get executed.
comment:12 Changed 12 years ago by
I had similar problem with animate function, and it took me quite a while to repeair the whole slideshow application, I've created, which broke after moving to the latest release of jQuery. It's quite serious bug in my opinion since, most "autocycle" applications will stop to work properly in chrome. I am wondering why it was classified as low priority bug and closed with wontfix flag?!
comment:13 Changed 12 years ago by
Keywords: | needsreview added |
---|---|
Resolution: | wontfix |
Status: | closed → reopened |
This has been closed as wontfix because really the choice here is we can nix requestAnimationFrame and go back to setInterval for all browsers and miss out on the performance/smoothness advantages or document that users should use callbacks instead of stacking animations blindly. If this is a problem for enough people, we will probably have to switch back to setInterval or find some glorious hack that fixes this bug without too much code and keeps requestAnimationFrame, but I cannot think of a viable solution.
comment:14 Changed 12 years ago by
Status: | reopened → open |
---|
comment:15 Changed 12 years ago by
I think the writing is on the wall; it seems more likely that the other vendors will follow Chrome than for Chrome to revert this behavior. By not doing requestAnimationFrame
animations when a user cannot see them, the browser is saving CPU time -- and more importantly, battery life in mobile devices.
timmywil is right that "blindly stacking animations" is a bad idea. For example if you want to show a slide for 10 seconds, have the callback for the completion of showing of the slide set a timeout for 10 seconds or use .queue()
to queue the timeout. Don't have some unrelated code just blindly set a 10-second timeout and force a transition regardless. If the user minimized your slide show, they HAVE NOT SEEN THE SLIDES.
comment:16 Changed 12 years ago by
Replying to anonymous:
Adam: That is not a good solution because it will stop the animation even if the user is able to see the window. You could have 2 windows (or more) running side by side but without having focus.
I agree, this is the wrong way to solve the problem.
Wasnt it one of the ideas behind jQuery in the first place that you shouldnt have to do "hacks" and write browser specific code to make it work ?
I'm not sure how any of this constitutes a need to write a "hack" to support a browser. You are instead updating your code to support new versions of jQuery and the improved handling of animation timers that will save your laptops battery life when you tab out of that cycling page...
I dont want to mix my transition logic with my "autoplay" (cycle) logic as they are seperate things and dont belong together. The animation logic should also work when not set to automatically cycle between elements, so user can just click the element they want to go to.
So, I fail to see how this is a problem... Make your "animation" function accept a callback that gets passed along to the actual .animate()
- problem solved. The situations where you don't care about getting a callback are still fine, because your "complete" callback will be undefined:
function animateElementIn( in, done ) { // assuming current is already set somewhere... current.fadeOut( 1000 ); in.fadeIn( 1000, done ); } function loopyCycler() { animateElementIn( next, function() { setTimeout( loopyCycler, 2000 ); }); }
The work arounds suggested earlier is not great.
Explain any issues with my earlier examples, and I might take the time to write a better work around for your specific needs. Oh, and personally, I think using setTimeout
or setInterval
to queue "cycles" is "not great" you should have been using the .animate()
callback or .queue()
in the first place...
comment:17 Changed 12 years ago by
In some applications the problem is really serious.
Using a callback function as a workaround is nice for a simple predefined things, but that is not always possible.
For example, if you have a game application where there are a lot of timeouts and intervals based on previous user input and game state. Using animations instead of timers? That's crazy.
A nice solution is to drop animations at all in inactive state.
But how to easily define if the state is inactive and animation won't play?
I suggest adding some property/function to determine that.
To make it possible things like
if(IsAnimationsRunning) $(something).animate(...); else $(something).clearQueue();
comment:18 Changed 12 years ago by
Summary: | setTimeout and fadeIn/fadeOut blocking in Chrome → animations halt when the browser is out of focus due to requestAnimationFrame |
---|
comment:21 Changed 12 years ago by
This is an additional approach to managing things that might happen when the window has lost focus:
http://jsfiddle.net/rwaldron/KK5mP/
based on a request in -dev
comment:22 Changed 12 years ago by
https://github.com/jquery/jquery/pull/436 - Pull request to implement a way to disable rAF and revert to setInterval always.
comment:23 Changed 12 years ago by
https://github.com/gnarf37/jquery/compare/jquery:master...ticket_9381_2 - Here is yet another possible solution to the problem inspired by a jaubourg comment in -dev.
comment:25 Changed 12 years ago by
I not sure, just a jquery n00b but I had this problem in google chrome when opening another tab. When returning to that tab the animations ran wild. Just stumbled on to this tip: http://www.learningjquery.com/2009/01/quick-tip-prevent-animation-queue-buildup
seems simply adding the .stop() solves all problems, at least for me it did, in two completly different animation scripts even. Just my 2 cents.
comment:26 Changed 12 years ago by
I'd like to give this bug a try as well. I know of a better solution to prevent this behavior and would like to see if it can be applied here.
louisremi
comment:27 Changed 12 years ago by
PR is here: https://github.com/jquery/jquery/pull/446
Animation frameworks have to deal with the problems that occur when requestAnimationFrame powers the animation loop and the tab loses focus. The general idea is to artificially limit the interval between two animation ticks. This was illustrated in Seth Ladd's "Intro to HTML5 Game Development" during Google I/O 2011: http://io-2011-html5-games-hr.appspot.com/#36 He calculates the time delta between two animation ticks and limit this value to 60ms
var dT = Math.min( now - prevNow, 60 );
The way jQuery animations work is more complex than Seth's ones, as animations aren't based on the time delta between the previous tick and the current one, but between the beginning of the animation (startTime) and the current one. The only way to limit the interval between two ticks is to move the value of startTime forward in time.
With this 5loc patch, jQuery detects when the interval takes longer than usual and moves startTime accordingly to effectively pause the animation. Once the tab is focused again, the animation continues where it stopped.
comment:29 Changed 12 years ago by
We've discussed the raf issue in the monday meeting and have decided to remove raf until it is both fleshed out further by all of the browser vendors and we have reliable solutions to the problems that come with supporting a requestAnimationFrame animate. This does not rule out the possibility of it's eventual permanent inclusion into jQuery's animate in either 1.7, or possibly 1.8, but we unknowingly opened up a can of worms by adding this too soon and we need to clear the detritus.
comment:30 Changed 12 years ago by
Owner: | set to Timmy Willison |
---|---|
Status: | open → assigned |
comment:31 Changed 12 years ago by
Milestone: | 1.next → 1.6.3 |
---|
comment:32 Changed 12 years ago by
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Remove requestAnimationFrame support. Fixes #9381.
Changeset: 2053d1c621e8ef65b79d6b339d7336c732ed1b82
comment:34 Changed 11 years ago by
Could this be a solution?
Using a plugin like https://github.com/mathiasbynens/jquery-visibility you could disable any animations while the window/tab isn’t focused, and re-enable them as soon as the window gains focus again. (The plugin uses the Page Visibility API and falls back to good old focus
and blur
in older browsers.)
I believe this is caused by requestAnimationFrame, which stops when the window is out of focus.
assigning to gnarf for assessment