Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
cerulean

Best practice for 'klling'/'undoing' Timelines of objects on rollover/out

Recommended Posts

This may be obvious, but I was wondering the best practice for dealing with Timelines on rollover/out states?  

 

Specifically I do a Timeline animation on rollover of an object (using jQuery's hover()) — on rollout I want to kill that timeline and tweenback — I use a simple TweenMax on the rollout because I don't simply want to reverse the Timeline (that would be the obvious and elegant solution), as the 'out animation' is different from the 'in animation'.

 

The best I could come up with is to store the Timeline in the object, and then kill it on rollout (myTImeline.kill()) and then do the 'out animation'.

 

Is there a more elegant way?  I take it because a Timeline is a complex thing that there's no equivalent to TweenMax's 'kill all tweens of this object'?

Link to post
Share on other sites

Hello cerulean, and welcome to the GreenSock forum!

 

Using TweenMax:

 

Here is and example of roll in and roll out:

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


 

// Hover events
$(document).on("mouseenter", "#wrapper", function(){
  
    var $overlay = $(this).children("#overlay");
  
    TweenMax.to($overlay, 0.9, {autoAlpha:1, ease:Power4.easeInOut});
  
}).on("mouseleave", "#wrapper", function(){
  
    var $overlay = $(this).children("#overlay");
  
    TweenMax.to($overlay, 0.9, {autoAlpha:0, ease:Power4.easeInOut});
  
});

:

It is just a simple tween for mouseenter (roll in) and mouseleave (roll out) .

 

Using TimelineLite:

 

This example uses TimelineLite:

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


 

var tl = new TimelineLite();
	
$(document).on("mouseover", "#wrapper", function(){  
       
        var $overlay = $(this).children("#overlay");

	tl = new TimelineLite();
        tl.to($overlay, 1, {autoAlpha:1, ease:Power4.easeInOut});

}).on("mouseout", "#wrapper", function(){    
        
        var $overlay = $(this).children("#overlay");

	tl = new TimelineLite();
        tl.to($overlay, 1, {autoAlpha:0, ease:Power4.easeInOut});

});

:

There are other ways to do it by playing and reversing the tween on Roll Out and Roll In.

 

Using TimelineLite ( play and reverse ) :

 

Here is an example that uses play and reverse:

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


 

var tl = new TimelineLite({paused:true});
tl.to($("#overlay"), 0.5, {autoAlpha:1, ease:Power4.easeInOut});
	
$(document).on("mouseover", "#wrapper", function(){  
       
	tl.play();       

}).on("mouseout", "#wrapper", function(){    
    
	tl.reverse();

});

:

Another helpful example by Rodrigo that use play and reverse:

See the Pen vjGxH by rhernando (@rhernando) on CodePen

 

I hope this helps! :)

  • Like 3
Link to post
Share on other sites

The second example is Timelines, of course — sorry — 

 

But then does the Overwrite manager handle Timeline-instantiated animations as well?

Link to post
Share on other sites

Hi,

 

The overwrite manager handles every GSAP instance you create that conflicts with the overwrite settings you have, even delayedCall(), feature that has get me out of a couple of jams.

 

Adding to Jonathan's great advice, there is one thing you should consider in this scenarios. If you create a new GSAP instance on every event handler you're consuming some CPU resources on it. Sure the overwrite manager takes care of the previous one and sends it to garbage collection so you're home free when it comes to memory leaks and CPU choking, sure in terms of CPU performance GSAP took a huge step in the right direction since version 1.12, but still this could harm UX when is not completely necessary, specially on underpowered devices. Then the new instance gets allocated in memory and there's no worries. But if for some reason later on you reverse that instance, you'll have quite a mess, because that instance will record the element's properties starting values and those could not necessarily be the initial or final render values you have in mind, so you'll end up with an animation that's a bit odd.

 

On the other hand if you create one tween/timeline, store it in the node element (like is done in the card flip sample) and then reference that instance on the event handler, no matter what the initial and final values will be the same and you won't have to use any extra resource to create and kill those instances.

 

This, IMHO, are best practices when it comes to a device/performance first approach when you're building a site/app, I've used them all the time with very good results in small-end devices and when you have a browser with a lot of other tabs opened.

 

Just my two cents.

  • Like 4
Link to post
Share on other sites

Thanks — of course!  I've been too long away from real coding (doing CSSy/HTMLy stuff) — 

 

Doh!

Link to post
Share on other sites

 

UPDATE:

 

I must say I'm having a lot of trouble with this.  If I simply reverse the timeline stored on each object (as per example above) it works fine. 

 

But what do I do if I want a different animation on rollover and out?  I tried storing TWO TimelineMax's on each object, one for rollover and one for rollout but that didn't work very well — I got all sorts of spurious movements and timelines not starting or ending properly.  Even when I explicitly paused one, played the other from 0, etc —  it seemed things were getting confused.  I could mock up a code pen but before I do, am I missing something here?  Can you have two timeline's for one object like this, competing, as it were?  Should you KILL the one when the other starts?  Will this release it for GC?  Color me confused!

 

 

Link to post
Share on other sites

If you are doing different animations on over / out, you will most likely have to create a new animation every time one of those events happens as the engine will have to record and use starting values that exist at the time the interaction takes place. This could be a tricky endeavor especially if each timeline is fighting for control over the same properties. I really think a very simple CodePen demo will help, especially as I'm having trouble understanding what type of different animation you would do on roll out.

Link to post
Share on other sites

OK, here's the code pen for it not working — I can do one for it working if you want, but that was simply creating a new timeline on each rollover and then new tweenmax on each rollout.

 

I attached some fake image urls  -- it's simply social media icons

 

See the Pen BDhCf by anon (@anon) on CodePen

Link to post
Share on other sites

Hi,

 

I believe I understand the issue now.

 

The problem with the code of your latest codepen is that if the timeline is not finished and the event handler is triggered again more instances are being created and added to the timeline. Also you're creating a conditional to see if the tl variable is empty or not, that's not very reliable and is also causing problems because what happens if there is a timeline stored in the variable?. I believe this code is all you need:

function growSocialMediaImage()
  {
    var tl = new TimelineMax();
    
    tl
      .to(this, 0.2, {zIndex: 100,rotation: "8deg",scale: 2,ease: Quad.easeInOut})
      .to(this, 1, {rotation: "0deg",scale: 1.5,ease: Elastic.easeOut})
      .to(this, .2, {borderRadius: "50%",ease: Quad.easeOut}, 0);
  }
  
function shrinkSocialMediaImage()
  {
    var tl = new TimelineMax();
    
    tl.to(this, 1.35, {zIndex: 0,rotation: "0deg",borderRadius: 0,scale: 1,ease: Elastic.easeOut});
  }
Link to post
Share on other sites

Thanks — but isn't this creating a new Timeline on each rollover/out, which you objected to a few posts back?

 

 

I was also a bit confused when you said 

 

 

 

 Also you're creating a conditional to see if the tl variable is empty or not, that's not very reliable and is also causing problems because what happens if there is a timeline stored in the variable?.

 

 

Isn't it standard coding practice to do something like this (in pseudocode)?

if ( myObject.myTimeline == null )
   myObject.myTimeline = new Timeline;
}

myObject.myTimeline.to(....);

etc....

I'm assuming that Timeline's and TweenMax's remain in the stored variable my object until I set all references to them to null, no?  i.e. I can play them, rewind them, reverse them, etc forever and any test whether they are == null will let me know if they are still there?  This is how it was in AS3, where I come from…

Link to post
Share on other sites

Hi,

 

Yep that turned out very hard to understand, sorry about it. What I was referring to is this:

function growSocialMediaImage() {
        var tl = this.timelineIn;
        if (typeof tl == 'undefined' || tl == null) {
            tl = new TimelineMax({
                paused: true
            });
            //more code
        }
        tl.play(0);
    }

That conditional statement will always be true, because you're checking if the var is undefined after you added a timeline instance to it. I think that this was causing some troubles as well. If you check the last code I posted every conditional logic is removed because I didn't thought they'll be useful. Of course I'm unaware of the complexities of your real code, so I'm might be wrong.

  • Like 1
Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

×