Jump to content
GreenSock

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

Invoking kill on a paused timeline wreaks havoc

Recommended Posts

Hello everyone,
 
I am currently creating a page that uses the GreenSock animation library to trigger animations as a user scrolls down the page. The interesting kink in this is that I need to swap out timelines I have created for the sake of responsive design (ie the animation graphics change to less complex ones when the page gets smaller.) This is a simplified test case from my actual page, but gets the same point across.
 
The primary issue I am having is with using .pause() and instantiation of a new TimelineMax() that uses the same elements. For example, if I create a timeline with a Tween, and then pause it:
 

var t1 = new TimelineMax();
t1.add(TweenMax.from($('#element1'), 1, {
                                scale: '0.2',
                                autoAlpha: 0,
                                transformOrigin: 'center center',
                                ease: Back.easeOut
                            }), 0);
t1.pause(0); // reset to beginning and hold, don't play yet.
 
I can then play the animation back later on using .play() or .restart() and it works fine. However, let's say I never actually play the animation, and then kill this timeline, and attempt to create a new one that attaches to #element1:

// so, instead of t1, we want to run t2 instead, so kill t1
t1.kill();


// create t2 instead
var t2 = new TimelineMax();
t2.add( 
        TweenMax.from($('#element4'), 2, {
            x: '-5px',
            ease: Power2.easeOut
        }), 0)
    .add(
        TweenMax.from($('#element3'), 2, {
            x: '-10px',
            ease: Power2.easeOut
        }), 0)
    .add(
        TweenMax.from($('#element2'), 2, {
            x: '-20px',
            ease: Power2.easeOut
        }), 0)
    .add(
     // Note element1 is reused here
        TweenMax.from($('#element1'), 1, {
            x: '-5px',
            y: '20px',
            scale: '0.2',
            autoAlpha: 0,
            transformOrigin: 'left bottom',
            ease: Back.easeOut
        }), 0.5))


// attempt to run t2
t2.play();
 
In this case, t2 will never run the animation/TweenMax on #element1 if t1 never left the paused state. However, if t1 was ever actually played with t1.play() before the t1.kill(), t2 will work successfully. I'm not sure how this works internally with GreenSock or if there is a better way I should be changing out animation timelines on the same objects. This only seems reproducible if .pause() is called, and .restart() / .play() is never called on the timeline originally containing (in this case) #element1.
 
Does anyone have a workaround or a better method for me? I've been banging my head on this for hours and have come to the conclusion it's either post here or start digging into GreenSock source.
Link to comment
Share on other sites

It's pretty difficult to troubleshoot blind (without a reduced test case codepen or jsfiddle), but I'm guessing this has something to do with the fact that from() tweens render immediately by default and of course they use the *current* values as the destination ("to") ones. Thus, if you create multiple from() tweens on the same element, you'll run into a logic problem (not a bug in GSAP). For example, let's take something super simple:

var obj = {x:0}; //obj.x starts at 0
TweenLite.from(obj, 1, {x:100}); //immediately makes obj.x 100, and records the current value (0) as the destination. 
TweenLite.from(obj, 1, {x:100}); //immediately makes obj.x 100, and records the current value...100 because of the previous line...as the destination. 

So when you run this code, you'll just see obj.x sit at 100 and never animate. That's exactly what it's logically supposed to do (no GSAP bug), but the author might be confused thinking "wait, shouldn't obj.x go from 100 to 0?"

 

from() tweens can be pretty useful, but they can also be a source of confusion in cases like this. You might have better luck if you use fromTo() tweens, or just to() tweens just because people generally think in those terms better. The whole "use the current value as the destination value" can cause logic hiccups like this. 

 

Another option is to set immediateRender:false on your from tweens, but the risk there is that you'll see a brief flash of the original values until the next tick. That could be mitigated by calling TweenLite.render() after you've run all your logic and you've got things set up the way you want. But frankly, I think you might find it easier to work with fromTo() or to() tweens. 

 

A 3rd option is to force your timeline to completion before killing it, assuming you've only got "from" tweens in there (solely for the purpose of making the elements return to their original positions). 

 

Does that help at all? 

Link to comment
Share on other sites

UPDATE: I think I found the solution:

 

Because I am using the .from() tween, setting t1's position to its final position BEFORE killing it, seems to solve the problem. The kill() does not reset the applied transform values on elements! So now my sample code would look something like this:

 

var t1 = new TimelineMax();
t1.add(TweenMax.from($('#element1'), 1, {
                                scale: '0.2',
                                autoAlpha: 0,
                                transformOrigin: 'center center',
                                ease: Back.easeOut
                            }), 0);
t1.pause(0); // reset to beginning and hold, don't play yet.
 
// so, instead of t1, we want to run t2 instead
// set elements back to where they should be to start another animation
t1.seek('-=0', false);
// kill t1
t1.kill();
 
// create t2 instead
var t2 = new TimelineMax();
t2.add( 
        TweenMax.from($('#element4'), 2, {
            x: '-5px',
            ease: Power2.easeOut
        }), 0)
    .add(
        TweenMax.from($('#element3'), 2, {
            x: '-10px',
            ease: Power2.easeOut
        }), 0)
    .add(
        TweenMax.from($('#element2'), 2, {
            x: '-20px',
            ease: Power2.easeOut
        }), 0)
    .add(
     // Note element1 is reused here
        TweenMax.from($('#element1'), 1, {
            x: '-5px',
            y: '20px',
            scale: '0.2',
            autoAlpha: 0,
            transformOrigin: 'left bottom',
            ease: Back.easeOut
        }), 0.5))
 
// attempt to run t2, which should now play
t2.play();
  • Like 1
Link to comment
Share on other sites

Does that help at all? 

 

Just as you wrote this I had come to the same conclusion. But yes, you're entirely right. See my solution above. Thanks for your help. I caused myself a ton of frustration with this one. Marking as solved.

  • Like 1
Link to comment
Share on other sites

Excellent, glad you figured it out. Thanks for letting us know. Sorry you had to bloody your head over this :) 

Link to comment
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.
×