Jump to content
Search Community

Dealing with infinite child timelines (start, stop)

MLM test
Moderator Tag

Go to solution Solved by Carl,

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

What is the best way to handle child timelines that have a infinite duration?

 

You can't just `TimelineMax.add` an infinite child timeline because they would block and cause the remaining events to never run in the root timeline.

 

The best way I have found is `restart`'ing the infinite child timeline in an `onStart` or `onComplete` event.  One problem I have run into with this method is, if you seek past when the child timeline gets initiated, then it will never run.

 

When using `start`, `stop`, `seek` on the root/parent timeline, how do you `start`, `stop` the child timeline appropriately?

 

 

 

Here is a basic example of an infinitely spinning ball that also translates around on the root timeline: 

let tl = new TimelineMax();

let ballNode = $('.ball');

// Set up the infinite rotating timeline
let alwaysRotateTl = new TimelineMax({
	repeat: -1,
	paused: true
});

alwaysRotateTl
	.fromTo(
		ballNode,
		2,
		{ rotation: 0 },
		{ rotation: 360, ease: Linear.easeNone }
	);


// Now do some stuff on the main timeline
tl
	.addLabel('red-label')
	.set(
		ballNode,
		{ backgroundColor: '#ff0000' },
		0
	)
	.to(
		ballNode,
		2,
		{
			x: 100,
			backgroundColor: '#00ff00',
			onComplete: function() {
				// Start the infinite rotate loop
				alwaysRotateTl.restart();
			}
		},
		'green-label'
	)
	.to(
		ballNode,
		2,
		{ y: 100, backgroundColor: '#00ffff' },
		'cyan-label'
	);

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

Link to comment
Share on other sites

@OSUBlake Thank you for the reply. Clever, but does have a flaw where you can pause at the beginning or any stage before it starts rotating, and then you press play which makes it prematurely start rotating.

 

Also, because `start`, `pause`, `seek` can be called anywhere, I would need to wrap each one with the proper child timeline play/pause and only use the wrapped version throughout. But this also falls apart with `addPause`. I don't use `addPause` but I am looking for something robust and doesn't require maintenance so that I don't have to remember to add it in some list or in the wrapped call in order to properly pause it.

Link to comment
Share on other sites

I changed the pause rotating issue with a label check, but maybe somebody has else a better way to do this.

 

When I work with objects that might not have any concept of each other, I like to use events to control them. Here's a demo I made that uses custom events. I found it too hard to directly control the growth of each leaf, so I just send out an event and let the leaves figure out if it is time to grow.

 

See the Pen gpGEye by osublake (@osublake) on CodePen

  • Like 1
Link to comment
Share on other sites

Hi MLM  :)

 

if i understood correctly , pls try this : 

 

tl.add(function(){ alwaysRotateTl.pause(0) })
  .set(ballNode,{backgroundColor:'#ff0000'})
  .to(ballNode,2,{x: 100,backgroundColor:'#00ff00'},'red-label')
  .add(function(){ alwaysRotateTl.play() })
  .to(ballNode,2,{y:100,backgroundColor:'#00ffff'},'cyan-label')
  .to(ballNode,2,{x:0,backgroundColor:'#ffff00'},'yellow-label');
  • Like 1
Link to comment
Share on other sites

  • Solution

hmm, my interpretation is that you just need a way to insert an infinite child so that it doesn't throw off where the "end" of the timeline is when you add other tweens. You also want to have the convenience of

 

- pausing / playing the parent does the same to the infinite child

- if you go to a time in the parent before the infinite-child's start time, the infinite child should not be playing or have any effect (in this case no rotation)

 

The easies approach is just to add() the infinite thing AFTER you build the rest of the timeline

 

tl.add('red-label')
  .set(ballNode, {backgroundColor: '#ff0000'}, 0)
  .to(ballNode, 2, {x: 100, backgroundColor: '#00ff00'}, 'green-label')
  .to(ballNode, 2, {y: 100, backgroundColor: '#00ffff'}, 'cyan-label')
  .to(ballNode, 2, {x: 0, backgroundColor: '#ffff00'}, 'yellow-label')
  .add(alwaysRotateTl, "cyan-label")//plop this in after all other tweens are added

http://codepen.io/GreenSock/pen/VLyvKY?editors=001

 

 

If you absolutely must create and add these things in order, you can also use recent().endTime() to fine the end time of the most recently added tween like

tl.add('red-label')
  .set(ballNode, {backgroundColor: '#ff0000'}, 0)
  .to(ballNode, 2, {x: 100, backgroundColor: '#00ff00'}, 'green-label')
  .add('cyan-label')
  .add(alwaysRotateTl)
  .to(ballNode, 2, {y: 100, backgroundColor: '#00ffff'}, 'cyan-label')
  .add('yellow-label', tl.recent().endTime()) //find end of most recently added tween
  .to(ballNode, 2, {x: 0, backgroundColor: '#ffff00'}, 'yellow-label')
  .to(ballNode, 1, {scale:0.5, backgroundColor:'#f60'}, tl.recent().endTime())//add another tween after the most recently added tween
  .to(ballNode, 1, {scale:2, backgroundColor:'#000'}, tl.recent().endTime() + 2)//add another tween 2 seconds aafter the most recently added tween ends

It's a bit more verbose, but handy.

 

http://codepen.io/GreenSock/pen/VLyvpL?editors=001

 

Docs for .recent()

  • Like 4
Link to comment
Share on other sites

@Diaco.AW

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

with your solution. The root timeline `pause` doesn't pause the child timeline. I also noticed that if you are at the beginning before it starts rotating, and then seek "yellow-label" it doesn't rotate. Although this works fine with the "cyan-label"(which is also after the `add` rotate function), so I am not sure what side-effect is happening there.

 

 

@Carl Your solution for adding infinite child timelines after everything else is built is awesome! I wish I didn't have to sacrifice potential clarity from that would come from having it in a more logical spot.

 

Perhaps a new keyword for the `TimelineMax.add` `align` option or a completely new option that would ignore the duration "so that it doesn't throw off where the "end" of the timeline".

// New `align` option keyword
// does collide with existing keywords, would need `ignore-sequence`, `ignore-start`
tl.add(childTimeline, '+=0', 'ignore');

// Or new parameter `blocking` or `isBlocking`
tl.add(childTimeline, '+=0', 'normal', 0, false);

// Maybe a new method
tl.addNonBlocking(childTimeline);
tl.addIgnored(childTimeline);

I really appreciate you laying out all of the possible options. Just to address your `tl.recent().endTime()` solution, very cumbersome to always have to reference a specific position from after the infinite child is added. This could also be cleaned up by putting the rest of the timeline in its own timeline if it didn't need to reference any labels in the root.

Link to comment
Share on other sites

Hi MLM

 

Thanks for the suggestion, we added recent() specifically for this use case and very carefully discussed changing the API so that you could specify whether or not new tweens, callbacks, labels get added to the end of the timeline or at the end of the last tween. We were going to create an AppendMode property for timelines which you could use to dictate whether new things got added at the end of the timeline or after the most recent thing.

 

It boiled down to the fact that such a change would really clutter and confuse the API. Every method that uses a position parameter would need to be adjusted and we would have to explain how the position paramater's behavior would change based on some other setting in the Timeline. 

 

For instance to explain this tween

TweenLite.to(obj, 1, {x:100}, "+=1");

In the tween above the position parameter is setting the insertion point to be 1 second after the end of the timeline or 1 second after the previous tween ends depending on what the value of appendMode is on the parent timeline.

 

And although we considered doing it on a tween by tween basis by allowing an additional parameter like isBlocking or ignore, that would just bloat the API and then people would be complaining that typing "ignore" on each tween is cumbersome. 

 

If you look at the existing flexibility of the position parameter (relative, absolute, at label, relative to a label) as shown here: http://greensock.com/position-parameter. Its pretty marvelous what it can do already. Adding infinite animations to timelines is not very common and when it needs to happen, there are existing solutions that will work. When considering the impact to the API it just didn't make a lot of sense to add another layer of confusion.

 

if recent().endTime() is too cumbersome for the few times its needed, you could write your own function perhaps

 

 
tl.to(obj, 1, {x:100}, end(tl));

function end(tl) {
  return tl.recent().endTime();
}
You can read the discussion here:

 

http://greensock.com/forums/topic/10617-add-tweenlabel-after-the-last-one-added-not-at-the-end-of-the-timeline/page-2#entry42565

 

Thanks again for the feedback. Hopefully the suggestions presented will be suitable.

  • Like 3
Link to comment
Share on other sites

@Carl Thank you for the insight into the decisions!

 

Just to be clear, I like your solution of adding them onto the very end after everything else is built mentioned in Post #6 the best so far.

 

I think infinite timelines are a big use case. Although I am new to GSAP and I am still working on my first animation. I am using it to keep a flame flickering constantly while other stuff continues to go on. Hopefully the way I tackled this problem with an infinite timeline, doesn't turn out to be a XY problem.

 

But I can also see it for a buzz-saw rotating, a hand poking/hinting at pushing a button over and over, sun continuously rotating its rays, character blinking, ambiance light flicker, a clock in the background, tail wagging, step-by-step instructions that repeat over and over before you press "next", etc. While some of those can be replaced with a CSS animation, GSAP's power and control would be lost. 

 

The reason `tl.recent().endTime()` becomes cumbersome, even after being wrapped up nicely, is because everything has to have a explicit position after the infinite timeline gets added. It becomes a fiasco of having to have explicit positions and `.addLabel` everywhere. 

 

 

Can a plugin wrap up this non-blocking infinite timeline functionality? As long as it is possibly to shove the gritty details behind a library and have everything work awesome, this could be a great solution.

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