Jump to content
GreenSock

james.brndwgn

Infinite looping nested timeline blocks main timeline

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

I've got a number of timelines which I want to control via a master timeline. This all works great, except for when one of those nested timelines is infinitely looping. It blocks the main timeline. I figure this must be because the time of the infinite timeline is, well... infinite, therefore the main timeline has no cue to continue, but how does one get past this?

See the Pen VbyNGq by getreworked (@getreworked) on CodePen

Link to comment
Share on other sites

I have had some luck replacing .add(stairsIdle.play(), 'stairsIdle') with .call(stairsIdle.play(), 'stairsIdle') but get a TypeError of Uncaught TypeError: callback.apply is not a function which is worrying.

Link to comment
Share on other sites

You need to find the end time of the last item.

mainTL
  .add(stairsTL.play(), 'stairs')
  .add("luggage", stairsTL.recent().endTime(false))
  .add(luggageTL.play(), 'luggage')

 

See the Pen qmxpdj?editors=0010 by osublake (@osublake) on CodePen

 

  • Like 1
Link to comment
Share on other sites

 

3 hours ago, james.brndwgn said:

.call(stairsIdle.play(), 'stairsIdle')

 

That's an error because stairsIdle.play() is not a function, it's a timeline. See all these gotchas when using .add(). A lot of them will apply to using .call().

 

 

Link to comment
Share on other sites

13 minutes ago, OSUblake said:

You need to find the end time of the last item.


mainTL
  .add(stairsTL.play(), 'stairs')
  .add("luggage", stairsTL.recent().endTime(false))
  .add(luggageTL.play(), 'luggage')

 

See the Pen qmxpdj?editors=0010 by osublake (@osublake) on CodePen

 

 

I see, so because stairsIdle is infinite there is no end time, so we add one (a time) via a label to continue the main timeline. Good trick to know. Thanks!

  • Like 2
Link to comment
Share on other sites

1 hour ago, OSUblake said:

 


mainTL
  .add(stairsTL.play(), 'stairs')
  .add("luggage", stairsTL.recent().endTime(false))
  .add(luggageTL.play(), 'luggage')

 

 

@OSUblake where does the .recent() comes from? If I log it, I get a timeline.

Link to comment
Share on other sites

For those that may come across this, while the above works well, if you're adding an infinite looping timeline to a main timeline it will then cause the main timeline to grow in time, which makes sense. Unfortunately, if you want to reverse the animation (for say a transition out) it will play back the entire main timeline which means if the looping timeline has been playing for awhile then the main timeline will have a lot of time to playback on. My solution to have an idle infinite looping animation that doesn't effect the main timeline, but can still be called when desired on the main timeline was to use .call with an anonymous function.

 

mainTL
  .add(stairsTL.play(), 'stairs')
  .call(() => {
    stairsIdle.play();
  })
  .add(luggageTL.play(), '-=1')

 

Link to comment
Share on other sites

1 hour ago, Dipscom said:

 

@OSUblake where does the .recent() comes from? If I log it, I get a timeline.

 

Recent returns the last thing added to the timeline, so it can be a tween, timeline, or callback.

 

And the code I posted only works if the last thing added is actually added to the end of the timeline. This will give you 2 as the end time, but it should be 5.

 

var tl = new TimelineMax()
  .to({}, 5, {})
  .to({}, 2, {}, 0);

var endTime = tl.recent().endTime(false); // => 2

 

To get the accurate end time, you would need to loop through all the children of the timeline.

 

 

 

  • Like 1
Link to comment
Share on other sites

Thanks.

 

Age must be getting to me. I went to the docs looking for it and did not spot the entry. Probably because it is siting right at the top of the list, not where I expected it to be, in alphabetical order. 

  • Like 1
Link to comment
Share on other sites

8 minutes ago, Dipscom said:

Thanks.

 

Age must be getting to me. I went to the docs looking for it and did not spot the entry. Probably because it is siting right at the top of the list, not where I expected it to be, in alphabetical order. 

If it's any consolation, I did the same thing earlier. Took me a minute to find it up at the top (was expecting alphabetical).

  • Like 1
Link to comment
Share on other sites

Maybe we will be needing to open a new branch in the forums: "Geriatric GSAP"...

  • Like 1
Link to comment
Share on other sites

1 hour ago, james.brndwgn said:

My solution to have an idle infinite looping animation that doesn't effect the main timeline, but can still be called when desired on the main timeline was to use .call with an anonymous function.

 


mainTL
  .add(stairsTL.play(), 'stairs')
  .call(() => {
    stairsIdle.play();
  })
  .add(luggageTL.play(), '-=1')

 

It's generally a good idea to keep infinite animations separate if you plan on reusing the timeline. 

 

And I would not use an anonymous function. When you reverse your timeline, it's going to call that function again, so your stairsIdle timeline will still be playing even if the element isn't visible. Inspect the element in your dev tools.

 

See the Pen oWEymv?editors=0010 by osublake (@osublake) on CodePen

 

A better approach might be to add and remove a callback when necessary.

 

See the Pen dWdKay?editors=0010 by osublake (@osublake) on CodePen

 

 

Link to comment
Share on other sites

51 minutes ago, Dipscom said:

Thanks.

 

Age must be getting to me. I went to the docs looking for it and did not spot the entry. Probably because it is siting right at the top of the list, not where I expected it to be, in alphabetical order. 

 

I had the same problem when I first heard about it. It's only like that for TimelineMax docs. In the TimelineLite docs, it's in alphabetical order. 

Link to comment
Share on other sites

I did bring up the alphabetical issue before, but nobody cared. Maybe now I can get @Carl 's attention with this fancy new mention feature.  :roll:

 

 

Link to comment
Share on other sites

Let's get the pitchforks out wave them angrily while shouting @Carl!

  • Like 1
Link to comment
Share on other sites

20 hours ago, OSUblake said:

 

It's generally a good idea to keep infinite animations separate if you plan on reusing the timeline. 

 

And I would not use an anonymous function. When you reverse your timeline, it's going to call that function again, so your stairsIdle timeline will still be playing even if the element isn't visible. Inspect the element in your dev tools.

 

 

A better approach might be to add and remove a callback when necessary.

 

 

 

 

Thanks for the tips @OSUblake. I took a slightly different approach with a reverseScene function which cleans things up as I transition to the next page. The code for anyone interested: 

   // Main timeline
    const mainTL = new TimelineMax({ delay: 1 });

    mainTL
      .add(stairsTL.play(), 'stairs')
      .add('stairsCallback')
      .add(playStairsIdle, 'stairsCallback')
      .add(limoTL.play(), 'stairs+=0.25')
      .add('limoCallback')
      .add(playLimoIdle, 'limoCallback')
      .add(luggageTL.play(), 'stairs+=0.5')

    function playStairsIdle () {
      stairsIdle.play();
    }

    function playLimoIdle () {
      limoParticlesIdle.play();
      limoSparklesIdle.play();
      limoBounceIdle.play();
    }

    function reverseScene (cb) {
      // Repeat 1 allows staggered animation to play out while we're reversing
      limoParticlesIdle.repeat(1);
      limoSparklesIdle.repeat(1);
      limoBounceIdle.pause();

      mainTL
        .remove(playLimoIdle) // Ensures we don't play this on reverse
        .remove(playStairsIdle) // Ensures we don't play this on reverse
        .reverse()
        .timeScale(2)
        .eventCallback('onReverseComplete', () => {
          // Kill all looping animations
          stairsIdle.kill();
          limoParticlesIdle.kill();
          limoSparklesIdle.kill();
          limoBounceIdle.kill();

          if (cb && typeof cb === 'function') {
            cb();
          }
        });
    }

 

Link to comment
Share on other sites

recent() has been fixed, we had a team of developers arguing for the past 2 years whether or not it should be at the top because it was recently created and was named recent(). They sorted it all out. 

  • Like 2
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.
×