Jump to content
Nier

Tween paused when switch to other tabs

Recommended Posts

Hello everyone,

 

I'm new to GSAP. I found that when I switch to other tabs, my tweens in timeline will stop to work (or say paused) until I switch back to original tab. That will be a critical problem because I made a music game by GSAP and Web Audio API. When I switch to other tabs, the music will continue playing but the animate won't, which cause the animation and the music are not sync.

I have searched some keywords like [pause, switch tab, timeline, tween] but found nothing. Can someone tells me how to fix this? Thanks a lot.

Share this post


Link to post
Share on other sites

Some would call that a "feature" :)

 

Keep in mind that most browsers throttle the requestAnimationFrame to 2fps or less in an inactive tab, so that could be the issue especially if you're using the latest version of GSAP (1.12.x) which has the lagSmoothing() feature enabled. Read about it at http://www.greensock.com/gsap-1-12-0-performance/

 

There are two things you could do:

  1. Force the engine to use setTimeout() instead of requestAnimationFrame so that the "ticks" remain relatively consistent even in inactive tabs by calling TweenLite.ticker.useRAF(false). The down side is that browsers are highly optimized to process animation-related code inside requestAnimationFrame ticks rather than setTimeout(), so you might see a slight performance degradation by turning it off (although frankly you probably won't notice it). Also, for mobile devices the fps throttling can help conserve battery power, so if you turn off requestAnimationFrame so that ticks are constantly happening in the background, it'll chew up more resources. 
  2. Turn off the lagSmoothing() feature by calling TweenLite.lagSmoothing(0). Of course the down side to doing that is you won't get the benefit of lag smoothing which can eliminate initial jerks in movement if the CPU gets bogged down. Plus lagSmoothing() prevents the jump that happens when you scroll on an iOS device and then release (which has nothing to do with any bugs in GSAP - it happens for all engines, as described in that article above).

Let us know if that resolves things for you. If I were you, I'd try #2 first and see if that solves it for you. A fancy option would be to sense when the browser tab becomes inactive and at that point turn off lagSmoothing() or set useRAF(false) and when the tab becomes active again, revert the settings. That way, you get optimal performance all the time. 

  • Like 4

Share this post


Link to post
Share on other sites

One thing you can do is to detect when tab is active or not, and to switch between RAF/setTimeout() 

 

window.addEventListener('blur', function() {
TweenLite.ticker.useRAF(false);
}, false);


window.addEventListener('focus', function() {
TweenLite.ticker.useRAF(true);
}, false);
  • Like 3

Share this post


Link to post
Share on other sites

Thanks a lot, here's my result.
 
I have tried TweenLite.ticker.useRAF(false), but it's no use, the animation still paused (or as GreenSock said, limited to very low fps). Then I changed to call TweenLite.lagSmoothing(0), and it worked! When I switch to other tabs and switch back, the animate is still sync with the music.
 
After lagSmoothing works, I want to do something like Chrysto said, but looks like blur and focus is not suitable for chrome. However, I think that's another question and not belong to this thread.
 
Thanks again, guys. I have not done many tests so far, but it looks fine for me now. I just wonder about the multi-browser support and mobile browser support (which html5 Web Audio API is total a disaster here).
 
EDIT.
I found that "blur" and "focus" actually works, but the TweenLite.lagSmoothing(0); is no use in that function. Which means TweenLite.lagSmoothing(0) is actually called when I switch to other tabs (I checked by put console.log() function in that), but the animation is still paused.
 

    window.addEventListener('blur', function () {
        TweenLite.lagSmoothing(0);
    }, false);
 
 
    window.addEventListener('focus', function () {
        TweenLite.lagSmoothing(1000, 16);
    }, false);

Share this post


Link to post
Share on other sites

Hello Nier,

 

Have you tried the event handlers: focusin (focus) and focusout (blur) instead..

// blur
window.addEventListener('focusout', function () {
        TweenLite.lagSmoothing(0);
}, false);
 
// focus
window.addEventListener('focusin', function () {
        TweenLite.lagSmoothing(1000, 16);
}, false);

Does that help? 

Share this post


Link to post
Share on other sites

Hi, I've tried these event, it doesn't work for my case.

 

It doesn't call focusout at switching tabs.

 

I have no idea how to do with this, because the action 'blur' and 'focus' looks very fit to my question (called blur when switch tabs and called focus when switch back), but the animate still paused...

 

Currently, I set lagSmoothing(0) when the DOM is ready and never set it back. I'll keep finding the solutions.

 

Thanks, guys. If I found some useful tips or solutions I'll edit my post. :)

Share this post


Link to post
Share on other sites

Nier,

  • Are you talking about switching Browser Tabs or switching tabs in a UI tab system within your web page?

If you are talking about switching between Browser Tabs, then you have to use the HTML5 Visibility API.. since focusin and focusout will only work for the Browser Window.

 

Look at this codepen I made that uses the new HTML5 Visibility API to check if the Browser Tab loses or gains focus.

 

Cross Browser HTML5 Visibility API example:

See the Pen sxgJl by jonathan (@jonathan) on CodePen

 

To Test focus of Browser tabs in my codepen example:

  • First click inside the Preview panel so the page gains focus (very very important)
  • Switch between tabs
  • Give another program focus and come back to the browser
  • Look at the above state changes.

Reference for HTML5 Visibility API:

// main visibility API function 
// use visibility API to check if current tab is active or not
var vis = (function(){
    var stateKey, 
        eventKey, 
        keys = {
                hidden: "visibilitychange",
                webkitHidden: "webkitvisibilitychange",
                mozHidden: "mozvisibilitychange",
                msHidden: "msvisibilitychange"
    };
    for (stateKey in keys) {
        if (stateKey in document) {
            eventKey = keys[stateKey];
            break;
        }
    }
    return function(c) {
        if (c) document.addEventListener(eventKey, c);
        return !document[stateKey];
    }
})();

Use the HTML5 Visibility API like this:

// check if current tab is active or not
vis(function(){
					
    if(vis()){
	
        // tween resume() code goes here
        // the setTimeout() is used due to a delay 
        // before the tab gains focus again, very important!	
	setTimeout(function(){            
            console.log("tab is visible - has focus");
        },300);		
												
    } else {
	
        // tween pause() code goes here
        console.log("tab is invisible - has blur");		 
    }
});

I also made it into a jQuery Plugin to help manage the focus of Browser Tabs & Browser Windows too:

 

HTML5 Visibility API jQuery Plugin:

See the Pen svIjb by jonathan (@jonathan) on CodePen

 

Hope this helps :)

  • Like 1

Share this post


Link to post
Share on other sites

Hi, Jonathan.

 

Thanks for you replying. I'm sorry that I made you misunderstanding what I mean.

I'll try my best to clarify the problem I met here.

 

What I want to do is to DISABLE the lagSmoothing, which make my Tween paused when I switch to other Browser tabs.

 

Calling lagSmoothing(0) when DOM is ready is fine. It will solve my problem. My tween will continue playing when I switch to other Browser tabs.

 

I want to do some advanced trick. What I want to do is trying to disable lagSmoothing "before" I switch to other Browser tabs, which should make my Tween continue playing, and enable lagSmoothing "after" I switch back to my original Browser tabs.

 

So I try above codes, including using

window.addEventListener('blur', function () {
    console.log("blur called");
    TweenLite.lagSmoothing(0);
}, false);

or using the HTML5 Visibility API you referenced, which is very awesome, BTW.

 

However, the problem is still there. My Tween is still paused when I switch to other Browser tabs, but the blur function is actually called. In other words, my javascript console do print the "blur called" message in above example. I'm very confused here and want to solve this.

 

Of course, my web page is not very heavy loading so enable or disable lagSmoothing looks no different. But I just want to know why this approach is not working here.

Share this post


Link to post
Share on other sites

Perhaps you are turning lagSmoothing() on too soon. Try using a delayed call to wait 0.1 seconds after the tab regains focus.

  • Like 1

Share this post


Link to post
Share on other sites

Yep.. Carl is right! .. If you look at my above code for the HTML5 Visibility API usage code block,  and/or

See the Pen sxgJl by jonathan (@jonathan) on CodePen

you will see that i have a setTimeout() of 300 milliseconds, because there is a slight delay when the browser tab regains focus. I used setTimeout(), but I could have used delayedCall() instead, as Carl had suggested.

  • Like 1

Share this post


Link to post
Share on other sites

Im trying to implement a similar issue using lagSmoothing(0); but have noticed that my delayed calls and TweenMax.to(elm, duration, {properties}); beecome out of sync when going to a different tab. The TweenMax.to runs in the background accordingly but the delayedCall (used as a interval counter) then starts slowing down and the intervals increase when off tab.

 

The  impObj.timers.ticker & the impObj.timers.autoChangeTimer become of out of sync but the impObj.timers.startBar stays in time.

 

NOTE: Im using knockout bindings to update some elements on delayedCall intervals.

function buildImpressionTimers() {
     delete choice.impressionObjs.timers.ready_timer;
     var impObj = choice.impressionObjs;
     impObj.timers.autoChangeTimer = TweenMax.delayedCall(impObj.settings.slide_time, autoChangeImpression, impObj]);
     impObj.timers.ticker = TweenMax.delayedCall(1, updateImpressionTime, [impObj]);
     impObj.timers.startBar = TweenMax.to(getElm(impObj.timerBar, '#choose-timer-bar'), choice.choose_total_t     ime(), {
        x: -getElm(impObj.timerBar, '#choose-timer-bar').width() + 31,
        ease: Linear.easeNone,
        onComplete: endImpressionSession
  });
}



function updateImpressionTime(impObj) {
   var time = choice.choose_total_time,
   state = choice.state();
   time(time() - 1);
   if (time() === 3) {
      if (impObj.bonusCount <= impObj.bonusCountLimit && impObj.bonus !== 'awarded' && impObj.bonus !== 'prevent   ') {
           impObj.bonus = 'award';
       }
       state.stage_percent(state.stage_total() - 1);
       animatePercentBar(0, 'increase', 1.5, 'all');
   }
   if (time() <= 1) {
        changeImpressionButton(getElm(choice.impressionObjs.timerButton, '#choose-buttons > .button'), '#dedede');
       choice.transitioning = true;
       disableAbout(1.5);
   } else {
       this.restart(true);
   }
}

Share this post


Link to post
Share on other sites

Would you mind providing a reduced test case as a codepen or jsfiddle? That'd really help.

 

The whole GSAP system is always completely synchronized, and by default it is driven by a single requestAnimationFrame loop which browsers throttle to about 2 per second when the tab is inactive (to reduce power consumption). If you DON'T want that (and most people do), you can tell GSAP not to use requestAnimationFrame like TweenLite.ticker.useRAF(false) in which case it'll use a setTimout() instead, and that doesn't get throttled by browsers. 

  • Like 3

Share this post


Link to post
Share on other sites

Hi,

 

I'm not sure if this explains what's happening in this case, but I remembered a great answer by Jamie some time ago, regarding how delayedCall() could cause some drifting and why a recursive delayedCall() or setTimeout could become a better option. Take a look:

 

http://greensock.com/forums/topic/11092-i-have-a-setinterval-function-in-which-i-have-many-element-positions-being-updated-is-there-a-way-i-can-benefit-from-gsap/#entry44579

 

If this doesn't help, as Jack pointed a live sample could be very helpful.

 

Rodrigo.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Recently Browsing   0 members

    No registered users viewing this page.