Jump to content
GreenSock

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

Repeating timeline woes

Recommended Posts

This code works fine on the first loop, and not on the second:

var tl = new TimelineMax({repeat:-1})
tl.fromTo(tx4,1,{skewX:-40, x:b.width},{skewX:0, x:0, ease:Linear.easeNone},'-=0.5')
  .to(tx4,0.5,{transformOrigin:'center bottom', skewX:70, scaleY:1.5, ease:Power2.easeOut},'-=0.1')
  .to(tx4,0.5,{skewX:-45, scaleY:1.25, ease:Power2.easeOut},'-=0.1')
  .to(tx4,0.5,{skewX:0, scaleY:1, ease:Back.easeOut},'-=0.1')

How can I fix this?

 

<rant> No matter how many times I run into this problem, it never seems to sink in. Looping timelines are so unintuitive in gsap that I could pull my hair out. It's soooo easy to do whatever you want with a timeline, but the moment you try to repeat it, it becomes soooo frustrating. It shouldn't even be called "repeat", because that would lead you to believe it's going to do the same thing it did the first loop. And I already know I'm going to get several responses explaining why this is so great and expected, smh</rant>

Link to post
Share on other sites

Sorry to hear about all the frustration. We definitely want to make timelines intuitive in GSAP, so feel free to help us understand how to make it better. But first, let me explain the dilemma...

 

Notice how you're creating conflicts within your timeline? You've got overlapping tweens of the SAME properties of the SAME object. How would you like the engine to handle that? Imagine a simple scenario like this:

TweenLite.to(element, 10, {x:100});
TweenLite.to(element, 5, {x:200, delay:2});

After 2 seconds, you've got two tweens that are fighting for control of element.x. Where should it go? One is telling it to go to 200 whereas the other is telling it to go to 100. Also notice that the 2nd tween would finish before the first. Here are some options for how the engine could handle that:

  1. Just let things run in the order they were created. In this scenario, on every frame render, tween1 would set element.x and then tween2 would set it to something different. Since the second one happened last, it technically "wins". However, there are two problems with this: First, it's wasteful performance-wise because the engine is double-setting the same property (one is a complete throwaway). The other [bigger] problem is that it can lead to very odd results - notice how in the example above the first tween finishes last. Therefore, if we use this technique, after 7 seconds, element.x will suddenly jump from 200 to somewhere around 80 (assuming element.x started at 0). Very awkward and unintuitive. 
  2. Have GSAP look for conflicts when it starts each tween, and if it finds one, kill the entire [older] tween. This avoids situations where things would suddenly "jump" at the end, but the down side is that there may only be portions of the tween that conflict (like they're both tweening skewX but tween1 is also tweening scale and tween2 is tweening rotation), so killing the entire [older] tween could be frustrating since you probably want the non-conflicting portions to continue uninterrupted.
  3. Have GSAP look for conflicts but ONLY kill the portions of the tweens that conflict (like just the "skewX" property). This seems like the most intuitive option because it gives the best of both worlds - it avoids "jumps" and it doesn't kill portions of the tweens that don't conflict. 

You can actually get any of the above behaviors in GSAP using the overwrite special property. To get behavior #1 from above, just set overwrite:"none". For #2, set overwrite:"all". #3 is actually the default behavior which is overwrite:"auto". In fact, you can change the default across the entire engine if you prefer, like this:

TweenLite.defaultOverwrite = "none";

Just be careful because that tells the engine to completely ignore conflicts by default, so it opens you up to potentially having things jump (but again, that'd only be caused by the way you are creating your tweens, so if you're mindful it should be fine). 

 

We're trying very hard to make things as intuitive as possible, and it seemed like overwrite:"auto" was the best option, but let us know if you can think of something better. 

 

So back to your scenario - things work the first time because overwriting analysis happens when each tween begins (another performance optimization), so the later tweens are overwriting (and killing) portions of the earlier tweens as they play. So when you repeat, those portions have been killed already and don't replay. 

 

We could, of course, roll back all the overwriting behavior and start afresh when a timeline repeats, thus technically you'd see the same thing every time through, but there's a pretty major trade off with that - not only would the engine take a performance hit due to all the extra recording of values, resetting overwrites, re-instantiating tweens, etc., but all that logic would eat up extra kb in terms of file size and runtime memory. That's a pretty major drawback in my opinion, especially for something that's pretty easy to work around when you understand what's happening. And I'd argue that it isn't really a shortcoming of GSAP - it's just a plain logic issue. And as you probably know, we place a HUGE priority on performance. Animation is one of the most important things to have run smoothly and efficiently. We've optimized the snot out of even the overwrite algorithms to make lookups fast. 

 

Sorry for the long response, but I wanted to make sure you understood the factors involved and could maybe help us identify a better way to make things perform as you'd expect. 

  • Like 2
Link to post
Share on other sites

In addition here is a little codepen example showing how you can manually apply overwrite:none to a tween that you know is going to overlap and conflict with a  previous tween:

http://codepen.io/GreenSock/pen/ceb8469594e961cd77a252f89f5755d4

 

Remove overwrite:none from the second tween and you will see that the first tween doesn't work on repeat (as you have experienced). 

  • Like 1
Link to post
Share on other sites

Also, I'm just curious - why are you using "-=0.1" as the relative placement (thus creating overlaps/conflicts)? This would all be solved if you didn't do that - you can just omit that parameter altogether and things would sequence perfectly one-after-the-other. 

 

Read on if you want to employ some ninja tricks (but this is totally not necessary...)

 

If you can reset all the values to the way they began, you could invalidate() your timeline before repeating it and then it'd cause everything to re-initialize just like the first time around. But again, you'd need to put the values back to their beginning states so that they start at the same spot.

 

One sneaky way to do that would be to leverage GSAP's ability to record starting values (which it must do when starting any tween), and just drop them into a paused timeline with some short tweens (it actually doesn't matter how long they are since you won't be doing the tweening anyway). Here's the concept:

//let's say you'll be tweening the skewX, skewY, scaleY, x, and transformOrigin of txt4 and txt5, we'll create a tween that mentions them all and make sure we set immediateRender:true so that it records the starting values immediately:
var originals = new TimelineLite({paused:true});
originals.to([txt4,txt5], 1, {skewX:1, skewY:1, scaleY:1, x:0, transformOrigin:"50% 50%", immediateRender:true});

//then, anytime you want to revert to the original values, you can do this (moving the playhead triggers the render):
originals.progress(1).progress(0); 

//and then to invalidate your timeline and make it run from the beginning as if it never played:
tl.seek(0).invalidate().play();
  • Like 1
Link to post
Share on other sites

Thanks for the responses, I'll check this all out tomorrow.

 

I had a thought of how the documentation could be improved BIG TIME. Diagrams! Simple line diagrams showing timeline interactions for various options. Everyone's brain is wired differently, mine is visually, so translating all this from words into mental pictures is challenging when we get into complex subjects. You guys wrote the engine, so you visualize it very easily and intuitively, but I'm trying to see what you see. I just thought that with all the time you guys spend in the forums, you might actually save yourselves a lot of time by improving the documentation in this way. Just a thought, don't worry about responding though, I'm just throwing it out there

Link to post
Share on other sites

Yeah, it was fixed as soon as I took out the timing offsets. Added that when I was playing with the effect, but didn't realize it would mess things up after repeating. Thanks for the help

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.

×