GreenSock for Beginners (Part 2): GSAP’s Timeline

Beyond CSS: Dynamic DOM Animation Libraries

Animating the DOM with GreenSock (GSAP) Part 2

The aim of this second part of GreenSock for Beginners is to introduce you to GreenSock’s TimelineMax. You’ll be learning:

  • Why you need a timeline
  • How to include multiple tweens in a timeline
  • How to package multiple timelines into functions and nest them inside a master timeline for greater flexibility.

By the end of this tutorial, you’ll be comfortable working with GreenSock’s timeline to manipulate and fully control multiple tweens.

For an introduction to the basics of GreenSock, including how to work with TweenMax for sequencing and staggering simple animations, head over to part 1 of this multi-part article.

The GreenSock articles are part of the series Beyond CSS: Dynamic DOM Animation Libraries. Here’s what I covered in the past instalments:

Why Would You Need GreenSock’s Timeline to Code Your Web Animations?

In Part 1, you learned how to add different animations to an element or multiple elements by creating a number of independent tweens and coordinating their timings with each tween’s delay property.

By default, stacking one tween after another results in all tweens happening at once.

What would be more helpful, however, is to be able to control when a tween is triggered with respect to other tweens, e.g., at the same time, 1 second or half a second before or after, etc.

Take this basic example with just two tweens. Here’s what happens:

  • Tween 1: a circle shape grows and shrinks as it rotates on its X and Y axes
  • Tween 2: some text pops up.

The GSAP snippet that makes it work looks like this:

// scale down the text 
// and hide it before the animation begins 
TweenMax.set('.example__title', { 
  scale: 0.2, 
  autoAlpha: 0
});

// scale the circle shape down before 
// the animation begins
TweenMax.set('.example__ball', {
  scale: 0.2
});

// tween 1
TweenMax.to('.example__ball', 0.5, {
  rotationX: 360,
  rotationY: 180,
  scale: 1,
  ease: Elastic.easeIn.config(2, 1)
});

// tween 2
TweenMax.to('.example__title', 0.5, {
  autoAlpha: 1,
  scale: 1,
  ease: Back.easeOut.config(4)
});

As you can see, both tweens happen at the same time, which is not the desired effect:

See the Pen GSAP Tutorial Part 2: Why the Timeline by SitePoint (@SitePoint) on CodePen.

If you want the text to appear just when the shape has stopped rotating, you’ll need to add an appropriate delay to tween2, like this:

// tween 2
  TweenMax.to('.example__title', 0.5, {
  // rest of the code here
  delay: 0.6
});

This works, but imagine you want to change the duration of the first tween, or the number of times it repeats. You’ll soon realize that the second tween doesn’t automatically wait for the first one to come to a close before starting. Therefore, you’ll need to adjust the delay‘s value on the second tween. With just two tweens and such a simple animation this won’t be much of a problem. Not so when your animations are more ambitious and the number of tweens grows.

That’s when you’ll be super happy to know that GSAP has you covered with its robust and flexible TimelineLite and TimelineMax, both included in TweenMax.

Coordinating Multiple Tweens with GSAP’s Timeline

Think of a timeline as a container for a number of tweens, or even other timelines. Inside a timeline, tweens lose their independence and become interconnected with one another. By default, each tween fires after the previous one has completed, it doesn’t matter if you change duration to any of your tweens or how many times they repeat.

As a first step into GreenSock’s timeline, try placing the tweens in the snippets above inside a timeline.

Here’s what the code looks like:

// instantiate TimelineMax
const tl = new TimelineMax();

// scale down the text 
// and hide it before the animation begins
tl.set('.example__title', { 
  scale: 0.2, 
  autoAlpha: 0
})
 // scale the circle shape down before 
 // the animation begins
 .set('.example__ball', {
  scale: 0.2
})
 // tween 1: rotate shape on X and Y axis
 // scale it up to its regular dimensions
 // add a fun ease
.to('.example__ball', 0.5, {
  rotationX: 360,
  rotationY: 180,
  scale: 1,
  ease: Elastic.easeIn.config(2, 1)
})
 // tween 2: make text appear and 
 // scale it up to its regular size
 // add a fun ta-da ease
.to('.example__title', 0.5, {
  autoAlpha: 1,
  scale: 1,
  ease: Back.easeOut.config(4)
});

Firstly, instantiate the timeline. You can opt for either TimelineLite or TimelineMax, depending on the features you need. TimelineMax has all TimelineLite’s features plus a few extras. You can find a complete list of what’s available in both modules in the TimelineMax dedicated page on the GSAP website.

The snippet above creates an instance of TimelineMax called tl. You can choose any name you like, it won’t affect the validity of your code.

Once you’ve got your instance, you can use most of the methods you’re already familiar with from Part 1 like to(), from(), fromTo(), etc.

The code above starts with adjusting a few values for your elements before any animation begins using set(). Just like you used set() with TweenMax, you can use the same method with the timeline to accomplish the same goals, i.e., setting the values of your elements’ properties so that change takes effect immediately, without the change over time which is typical of animation. You can read more about the use of set() in the dedicated docs’ page.

The rest of the code is not different from what you previously wrote using TweenMax, the only difference is that you’re now chaining TimelineMax methods. As you can see, GSAP’s syntax remains consistent throughout its implementations, which certainly helps the learning process.

The most important thing to notice is that now the text appears just after the shape has finished animating. But look at the code a bit closer, you didn’t need to use any delay property to accomplish this. As a matter of fact, you don’t need delays to coordinate your tweens any more, no matter how many other tweens you keep adding to your timeline.

See the Pen GSAP Tutorial: Simple Timeline by SitePoint (@SitePoint) on CodePen.

GreenSock’s TimelineMax Position Parameter

Having your tweens run in quick succession automatically is all well and good. However, what if you’d like to have one element animate just half a second before, or a couple of seconds after, the previous animation completes? All this without having to readjust other values in the overall animation.

That’s where the position parameter comes in. You add this parameter after the vars {} object using relative incrementation (-=0.5, +=2).

Adding -=1 triggers a tween 1 second before the end of the previous tween in the timeline, while +=1 will trigger a tween 1 second after the end of the previous tween in the timeline.

You can also use an absolute number, e.g., 1, as value for the position parameter. In this case, the value specifies the precise time in seconds you’d like your tween to start.

Here’s a simple example:

.to(box1, 1, {
  rotation: 45,
  transformOrigin: 'center bottom',
  ease: Elastic.easeOut
})
.to(box2, 1, {
  rotation: -45,
  transformOrigin: 'center bottom',
  ease: Elastic.easeOut
})

The snippet above shows two elements rotating in opposite directions inside a timeline.

Without a position parameter, box2 would start animating as soon as box1 completes its animation.

To make both tweens fire at once, add a comma after the closing curly brace in the second tween and a position parameter of '-=1', like so:

.to(box1, 1, {
  // code here
})
.to(box2, 1, {
  // code here
}, '-=1') //position parameter

Because the first tween lasts 1 second, a position parameter of '-=1' will anticipate the animation by 1 second, which will cause both tweens to fire at the same time.

Here’s the code above in the context of a longer animation using the position parameter.

Using Labels as Value for the Position Parameter

There is a more flexible and intuitive way of working with the position parameter: instead of just numbers, you can use labels, which you can add to the timeline and refer back to in your tweens.

The use of labels makes your code much more readable, something you’ll be thankful for in more involved animations.

Whenever you need a reference point for timing your tweens in a timeline, just add a label with some meaningful text using the .add() method of both TimelineLite and TimelineMax:

tl.add('nameoflabel');

Then, use the label as position parameter:

// move element horizontally 100px
tl.to(element, 0.5, {
  x: 100
})
.add('go') // add a label
// move element vertically 100px
// with reference to the 'go' label
.to(element, 1, {
  y: 100
}, 'go');
// rotate otherElement 
// with reference to the 'go' label
.to(otherElement, 0.5, {
  rotation: 360
}, 'go');

In the snippet above, element moves 100px to the right. As soon as this animation ends, both element and anotherElement animate at the same time, because they both fire with reference to the go label (just give labels a name that makes sense in the context of your animation).

You can also use labels with relative values. For example, if you want otherElement to fire 2 seconds after element, use this as position parameter instead: 'go+=2'.

In the following demo, notice how some of the animations, for example those relating to the robot’s hand-waving, mouth movement and speech bubble, are all coordinated using labels as position parameter:

You can learn tons more on the position parameter in this dedicated page on the GSAP website.

Read Modern JavaScript

Keep up-to-date with the evolving world of JavaScript

Master Timelines with GreenSock and Keeping Your Code Organized

Although the demos you’ve seen so far work fine for demonstration purposes, writing your code in the global scope is not best practice.

If your animation is quite simple, just package your timeline inside a function and call it at the appropriate time.

For more complex scenarios, GSAP’s super powers include a master timeline, that is, a regular timeline where you can nest other timelines. This setup works wonders in terms of keeping your code organized, maintainable, and flexible.

Here’s what a nested timeline looks like:

// timeline-based animation inside a function
function partOne() {
  // timeline instance
  const tl = new TimelineMax();
  
  // add your tweens to the timeline as usual
  tl.to(myElement, 0.5, {
    rotation: 90,
    ease: Back.easeOut
  })
    .to(otherElement, 1, {
    // more code here
  });
  
  // return the timeline instance
  return tl;
}

// create a new timeline instance 
const master = new TimelineMax();

// add your function and a label to it
master.add(partOne(), 'part1');

The snippet above shows a timeline-based animation tucked away in its own function. You can name the function in a way that describes that chunk of animation or simply partOne, sceneOne, etc., whatever makes more sense to you. You then instantiate GSAP TimelineLite or TimelineMax, add your tweens as you’ve done so far, and return the timeline instance: return tl.

Finally, you create your master timeline instance and use .add() to include your function making sure the function gets called (notice the brackets in the function name).

In the snippet I also added a label. This will come in handy when you want to control the master timeline as a whole. I’ll show you what I mean in the next section.

The cool thing is that now you can create more timelines inside functions and add them to the master timeline. So modularized, your code is a lot easier to understand and more flexible: you can change the order in which the timelines are called as well as their timing relationships in a snap.

Have a look at the demo below, which uses a master timeline to host an animation broken into four timelines, each in its own function:

See the Pen GSAP Tutorial: Nested Timelines by SitePoint (@SitePoint) on CodePen.

GreenSock’s Timeline Animation Tricks

GreenSock has some neat tricks up its timeline’s sleeve that will make your life as a web animator easier.

Here’s how.

How to Pause All Your GSAP Tweens on Page Load

A timeline puts your tweens in relation with each other, therefore it lets you control them as a whole.

Let’s say you’d like to pause all your tweens on page load because you’d like to start your animation on button click. GSAP’s timeline lets you do this in one line of code when you instantiate it:

const tl = new TimelineMax({ paused: true });

Similarly, you can pause nested timelines in one blow by pausing the master timeline:

const master = new TimelineMax({ paused: true });

How to Play, Pause, Restart, and Reverse Multiple GSAP Tweens as a Whole

In Part 1, you learned how to control individual tweens using play(), pause(), reverse(), restart(), and resume().

You can use the same methods to control an entire timeline as well as nested timelines (via the master timeline).

For example, to play the entire sequence of tweens inside a timeline, write:

tl.play();

You can see restart() at work with the timeline to implement the functionality of the Replay button in some of the live CodePen demos above.

How to Slow Down/Speed Up Multiple GSAP Tweens At Once

Wrapping your tweens inside a timeline will let you change the speed of your entire animation with only a tiny bit of code. For instance, to slow down your animation, you can write:

tl.timeScale(0.3);

or, if you have nested timelines:

master.timeScale(0.3);

The .timeScale() method scales the time of the animation. A value of 1 sets the time to the normal speed (default), 0.5 to half the normal speed, 2 to double the normal speed, etc.

Why is this useful? Because there will be times when you need to check what your animation looks like when played a bit faster, or you just want to check specific tweens closely in super slow motion. With GreenSock’s .timeScale() you’ll be able to do so without fiddling with the timings in each timeline.

How to Play Your Animation from a Specified Place in the Timeline with GSAP

Creating animations for the web means adjusting the same tween tons of times until it feels just right. However, if your tween is somewhere in the middle of a complex animation, or even worse, right at the end, replaying the entire animation over and over will soon wear you out.

Enters .seek(), your best friend. With GSAP’s .seek() method and the use of labels, you’ll be able to start your animation from any point in the timeline. The same goes for nested timelines inside a master timeline, which is why I added a label named part1 to the master timeline in the previous section above.

In particular, here’s a master timeline with 3 nested timelines:

const master = new TimelineMax();

master.add(partOne(), 'part1')
      .add(partTwo(), 'part2')
      .add(partThree(), 'part3');

Let’s say, there are some adjustments you’d like to make to the middle part of the animation, and to do so you need to test your changes quite a few times. Instead of playing the entire sequence of animations over and over, which could be a tedious job, write the snippet below and your animation will start exactly at ‘part2’:

master.seek('part2');

If, on the other hand, you’d like to start your animation 3 seconds after ‘part2’, use the label with a relative position parameter:

master.seek('part2+=3');

Awesome, you’re animation starts at the exact point where you want to make your adjustments and you can make any number of tests without pulling your hair out.

Conclusion

This article has shown you how to work with GreenSock’s TimelineMax() and with nested timelines. It should be clear the huge control this flexible and robust timeline library can give you when creating web animations, and how quick it can be to put together a sophisticated animation sequence with its help.

Stay tuned for Part 3 of this mini-series dedicated to GreenSock, where you’ll be experimenting with some awesome GSAP premium plugins on CodePen.

In the meantime, to familiarize yourself with the timeline, just fork one of the demos and tweak the position parameter values, add more tweens to the existing timlines, reverse their order, etc.

Then, why not create your own animations and share them with all of us in the comments?