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:
- Animating the DOM with Anime.js touches on how to make the best use of animation on the web and when you could consider using a JavaScript animation library instead of CSS-only animation. It then focuses on Anime.js, a free and lightweight JavaScript animation library
- Fun Animation Effects with KUTE.js introduces you to KUTE.js, a free and feature-rich JavaScript animation library
- Make Your Website Interactive and Fun with Velocity.js (No jQuery) shows you how to use Velocity.js, an open source, robust free animation library, to create performant web animations
- GreenSock for Beginners: a Web Animation Tutorial (Part 1) is an overview of GreenSock, also known as GSAP (GreenSock Animation Platform), where I discuss the library’s modules and licensing model. I also show you how to code a simple tween, sequences of tweens, and staggering animations using GSAP TweenMax.
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
});
See the Pen GSAP Tutorial Part 2: Managing Sequences with Delay by SitePoint (@SitePoint) on CodePen.
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.
See the Pen GSAP Tutorial: The Position Parameter by SitePoint (@SitePoint) on CodePen.
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:
See the Pen GSAP Tutorial: The Position Parameter with Labels by SitePoint (@SitePoint) on CodePen.
You can learn tons more on the position parameter in this dedicated page on the GSAP website.
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?
Frequently Asked Questions about GSAP’s Timeline
How can I control the speed of animations in GSAP’s timeline?
Controlling the speed of animations in GSAP’s timeline is quite straightforward. You can use the timeScale() method to control the speed of the animation. If you set timeScale(2), the animation will play twice as fast. Conversely, if you set timeScale(0.5), the animation will play at half the speed. Remember, this method affects the entire timeline, including all child animations.
Can I add labels to my GSAP timeline?
Yes, you can add labels to your GSAP timeline using the addLabel() method. Labels are useful for marking specific points in your timeline. You can then use these labels to position other tweens or even control the playback of the timeline. For example, myTimeline.addLabel(“start”) will add a label named “start” at the current time on the timeline.
How can I pause and resume animations in GSAP’s timeline?
GSAP’s timeline provides the pause() and resume() methods to control the playback of animations. You can call myTimeline.pause() to pause the animation at the current position. To resume the animation, you can call myTimeline.resume(). These methods are very useful for creating interactive animations where the user has control over the playback.
Can I nest timelines in GSAP?
Yes, GSAP allows you to nest timelines, which means you can create complex animations by combining multiple timelines. A nested timeline behaves like a single tween, so you can control it as a whole. You can add a timeline to another timeline just like you would add a tween.
How can I reverse animations in GSAP’s timeline?
GSAP’s timeline provides the reverse() method to play the animation backwards. When you call myTimeline.reverse(), the animation will start playing from the current position to the beginning. This is a powerful feature that allows you to create sophisticated animations with minimal code.
Can I loop animations in GSAP’s timeline?
Yes, you can loop animations in GSAP’s timeline using the repeat() method. For example, myTimeline.repeat(3) will play the animation 3 times. If you want the animation to repeat indefinitely, you can use myTimeline.repeat(-1).
How can I stagger animations in GSAP’s timeline?
GSAP provides the staggerFrom(), staggerTo(), and staggerFromTo() methods to create staggered animations. These methods work just like their non-staggered counterparts, but they also accept an additional parameter that specifies the amount of delay between each tween.
Can I sync multiple animations in GSAP’s timeline?
Yes, GSAP’s timeline allows you to sync multiple animations by adding them at the same position. When you add a tween or another timeline to a timeline, you can specify the position at which it should be added. If you add multiple animations at the same position, they will start playing simultaneously.
How can I create relative positioning in GSAP’s timeline?
GSAP’s timeline allows you to create relative positioning using the “+=” and “-=” syntax. For example, myTimeline.to(“.class”, 1, {x: 100}, “+=1”) will start the tween 1 second after the end of the previous tween.
Can I control the progress of animations in GSAP’s timeline?
Yes, GSAP’s timeline provides the progress() method to control the progress of animations. You can get the current progress of the animation by calling myTimeline.progress(), and you can set the progress by calling myTimeline.progress(0.5). This method is very useful for creating scrubbing effects.
Maria Antonietta Perna is a teacher and technical writer. She enjoys tinkering with cool CSS standards and is curious about teaching approaches to front-end code. When not coding or writing for the web, she enjoys reading philosophy books, taking long walks, and appreciating good food.