Jump to content
Search Community

Best practices for calling timelines from other timelines

Acccent test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

Hi all :)

 

I'd like to ask about the best, most efficient way to reuse a single timeline several times, from within other timelines.

First, here are some threads I found:

https://greensock.com/forums/topic/16303-reverse-a-child-timeline-of-a-modular-timeline-independently/

https://greensock.com/forums/topic/16333-re-run-tween-in-a-timeline-at-a-later-point-in-the-timeline/

https://greensock.com/forums/topic/16348-multiple-timelines-sharing-addplay-wont-play/

https://greensock.com/forums/topic/13241-nesting-timelinelite-multiple-times-doesnt-work/

 

Here's what I got from of all of that – for clarity, I'll be calling the timelines child (the one you want to reuse) and parent (the one that you want to embed into):

  • when just use add() with the name of child, you make child impossible to play in a different context, and you also can't add it twice. It basically becomes a single, complex tween inside parent and nothing else.
  • you can also add() a new linear tween of child using child.tweenFromTo(); that basically copies the content of child into a self-contained tween that's embedded inside parent.
  • you can use add() with a function that builds and returns a new child timeline; that way, you write the code once, but you create a new instance of child every time that gets added to parent.
  • subtly different to the above, you can add() or call() a function that builds a new child; you're also making new instances of the child each time, but here you're not adding it to parent, just playing it independently. So the function you call needs to play child but doesn't need to return anything.
  • finally, what I do in the linked codepen, you can build a child somewhere else, and then add() or call() a function that only plays it.

 

(Note: 'add' and 'call' are functionally identical I believe, 'add' just works with other things besides callbacks; and I'm not sure what the difference is between 'call' and 'addCallback'.)

 

First of all: did I get all of that right, or did I misunderstand something?

 

Second: is there one of these solutions that's preferred over the other ones? I picked the last one in the codepen because I feel like if you have a single child that you refer to each time, and play (or otherwise manipulate) that one when you need it, would be more memory-efficient. Of course, that means you can't have two parents playing at the same time, or you'd run into problems. Am I missing something?

See the Pen eVOJaj by Acccent (@Acccent) on CodePen

Link to comment
Share on other sites

Let me take this piece-by-piece....

 

Quote

when just use add() with the name of child, you make child impossible to play in a different context, and you also can't add it twice. It basically becomes a single, complex tween inside parent and nothing else.

 

It doesn't become something else (complex tween), no. But it's correct that a child can't exist inside or multiple parents at the same time. Just like a DOM node. Remember, an animation's playhead is always controlled by (and synchronized with) its parent timeline's playhead. 
 

Quote

you can also add() a new linear tween of child using child.tweenFromTo(); that basically copies the content of child into a self-contained tween that's embedded inside parent.

 

tweenFromTo() doesn't copy anything anywhere - it simply creates a tween that literally animates the "time" value of that timeline. That's it. So if you put 100 tweens into a timeline that do all kinds of movements and rotations or whatever, and then you call tweenFromTo() on that timeline, all of that animation data still only resides in that original timeline - there's just a single tween that gets returned that literally animates one property - the "time" - of that timeline. 
 

Quote

you can use add() with a function that builds and returns a new child timeline; that way, you write the code once, but you create a new instance of child every time that gets added to parent.

 

Sounds about right. There's a fantastic article about this technique at https://css-tricks.com/writing-smarter-animation-code/ (I'd highly recommend reading it). 
 

Quote

subtly different to the above, you can add() or call() a function that builds a new child; you're also making new instances of the child each time, but here you're not adding it to parent, just playing it independently. So the function you call needs to play child but doesn't need to return anything.

 

Yes, that's correct. This technique is fine in some cases, but keep in mind that you won't be able to scrub the timeline forward/backward if you structure things this way. You're just triggering actions that fire off animations that aren't embedded in a parent that'd make it possible to control them all as a whole. Again, that's perfectly fine in some cases - I just wanted to make sure you understood the tradeoff. 

 

I wouldn't say there's one way that the "right" or recommended way. It all depends on your goals. 

 

Also keep in mind that when a tween renders the very first time, that's when it records its start/end values internally. So if your SVG face starts normal and you build one "wink left" animation, and one "wink right" animation which will run based on the click of a button, think about how you want the animation to work - if each animation starts from the "normal" state and you're reusing the exact same timelines and just playing/reversing them on button clicks, imagine what'll happen if the user clicks the "wink left" button and halfway through that animation, they click the "wink right" button. In my opinion, the most natural thing would be to create a new tween/timeline that animates it from the current (partially winked) state to the next state rather than robotically jumping back to "normal" and animating from there. 

 

But if your goal is to always maintain exactly the same starting/ending states, it's wise to always reuse the same timeline (and/or animate its playhead via the tweenFromTo() method). 

 

Does that help?

 

  • Like 3
Link to comment
Share on other sites

Yep, all of this helps a lot, thanks! In my case, I'm indeed in a situation where the child timeline will always play uninterrupted till it reaches the end (or the start if it's playing backwards). But I understand how, if you need more flexibility, you're better off returning new instances of the child timeline.

 

To be clear, regarding .tweenFromTo(), if you had a something like

parent
  .add(child.tweenFromTo(0, 10))
  .add(child.tweenFromTo(0, 10), 5);

it would be the same child that's being tweened twice? what would happen during the overlap?

Link to comment
Share on other sites

Think of the tweenFromTo() stuff like instructions to move a needle on a record (music). It wouldn't make sense to write the code the way you did because you've got an overlap of 5 seconds where two hands are fighting for control of the needle's position, telling it to go to completely different places. So on a single tick, one tween says "render that timeline at 0.25 seconds" and immediately after that (literally on the same tick), the other one says "render that timeline at 5.25 seconds". Whichever one runs last would win, but it's just a bad idea to structure things like that (in my opinion). 

 

See what I mean?

 

 

  • Like 3
Link to comment
Share on other sites

Another (last, I hope) question about this:

 

What's the difference between

parent.call(() => { child.play(0) });

and

parent.call(child.play, [0]);

?

 

As far as I'm aware, there should be none, but my code works with the former and not the latter. How come?

Link to comment
Share on other sites

Ah, that's just a scope issue. In other words, in your first example, play() is being called directly on "child", but in your second example there is no scope declared. That's just how JavaScript works - it's unrelated to GSAP. To correct your second call, you can define the scope like:

 

parent.call(child.play, [0], child);

 

Does that clear things up? 

  • Like 4
Link to comment
Share on other sites

  • 2 years later...

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.
×
×
  • Create New...