Timeline
A Timeline is a powerful sequencing tool that acts as a container for tweens and other timelines, making it simple to control them as a whole and precisely manage their timing. Without Timelines, building complex sequences would be far more cumbersome because you'd need to use a delay
for every animation. For example:
// WITHOUT Timelines (only using tweens with delays):
gsap.to("#id", { x: 100, duration: 1 });
gsap.to("#id", { y: 50, duration: 1, delay: 1 }); //wait 1 second
gsap.to("#id", { opacity: 0, duration: 1, delay: 2 }); //wait 2 seconds
What if you wanted to make the first animation longer? You'd need to adjust every delay thereafter. And what if you want to pause()
the whole sequence or restart()
it or reverse()
it on-the-fly or repeat it twice? This could become quite messy, but GSAP's Timelines make it incredibly simple:
//WITH Timelines (cleaner, more versatile)
var tl = gsap.timeline({repeat: 2, repeatDelay: 1});
tl.to("#id", {x: 100, duration: 1});
tl.to("#id", {y: 50, duration: 1});
tl.to("#id", {opacity: 0, duration: 1});
// then we can control the whole thing easily...
tl.pause();
tl.resume();
tl.seek(1.5);
tl.reverse();
...
Now we can adjust the timing without worrying about trickle-down changes to delays! Increase the duration of that first tween and everything automatically adjusts.
For a quick overview of GSAP's timelines, check out this video from the "GSAP 3 Express" course by Snorkl.tv - one of the best ways to learn the basics of GSAP 3.
Positioning animations in a timeline
By default, animations are added to the end of the timeline so that they're sequenced one-after-the-other but you can use the position parameter to control precisely where things are placed. It typically comes after the vars parameter and it uses a flexible syntax with the following options:
- Absolute time (in seconds) measured from the start of the timeline, as a number like
3
// insert exactly 3 seconds from the start of the timeline
tl.to(".class", { x: 100 }, 3);
- Label, like
"someLabel"
. If the label doesn't exist, it'll be added to the end of the timeline.
// insert at the "someLabel" label
tl.to(".class", { x: 100 }, "someLabel");
"<"
The start of previous animation**. Think of<
as a pointer back to the start of the previous animation.
// insert at the START of the previous animation
tl.to(".class", { x: 100 }, "<");
">"
- The end of the previous animation**. Think of>
as a pointer to the end of the previous animation.
// insert at the END of the previous animation
tl.to(".class", { x: 100 }, ">");
A complex string where
"+="
and"-="
prefixes indicate relative values. When a number follows"<"
or">"
, it is interpreted as relative so"<2"
is the same as"<+=2"
. Examples:"+=1"
- 1 second past the end of the timeline (creates a gap)"-=1"
- 1 second before the end of the timeline (overlaps)"myLabel+=2"
- 2 seconds past the label"myLabel"
"<+=3"
- 3 seconds past the start of the previous animation"<3"
- same as"<+=3"
(see above) ("+="
is implied when following"<"
or">"
)">-0.5"
- 0.5 seconds before the end of the previous animation. It's like saying "the end of the previous animation plus -0.5"
A complex string based on a percentage. When immediately following a
"+="
or"-="
prefix, the percentage is based on total duration of the animation being inserted. When immediately following"<"
or">"
, it's based on the total duration of the previous animation. Note: total duration includes repeats/yoyos. Examples:"-=25%"
- overlap with the end of the timeline by 25% of the inserting animation's total duration"+=50%"
- beyond the end of the timeline by 50% of the inserting animation's total duration, creating a gap"<25%"
- 25% into the previous animation (from its start). Same as">-75%"
which is negative 75% from the end of the previous animation."<+=25%"
- 25% of the inserting animation's total duration past the start of the previous animation. Different than"<25%"
whose percentage is based on the previous animation's total duration whereas anything immediately following"+="
or"-="
is based on the inserting animation's total duration."myLabel+=30%"
- 30% of the inserting animation's total duration past the label"myLabel"
.
*Percentage-based values were added in GSAP 3.7.0
**The "previous animation" refers to the most recently-inserted animation, not necessarily the animation that is closest to the end of the timeline.
Position Parameter Interactive Demo
loading...
Special Properties and Callbacks
Add any of these to your vars object to give your animation special powers:
gsap.timeline({
onComplete: myFunction,
repeat: 2,
repeatDelay: 1,
yoyo: true,
});
autoRemoveChildren
Boolean IfautoRemoveChildren
is set totrue
, as soon as child tweens/timelines complete, they will automatically get killed/removed. This is normally undesireable because it prevents going backwards in time (like if you want toreverse()
or set the progress lower, etc.). It can, however, improve speed and memory management. The root timelines useautoRemoveChildren: true
.callbackScope
Object The scope to be used for all of the callbacks (onStart
,onUpdate
,onComplete
, etc.). The scope is whatthis
refers to inside any of the callbacks.delay
Number Amount of delay in seconds before the animation should begin.onComplete
Function A function that should be called when the animation has completed.onCompleteParams
Array An array of parameters to pass theonComplete
function. For example,gsap.timeline({onComplete: myFunction, onCompleteParams: ["param1", "param2"]});
.onInterrupt
A function to call when the animation is interrupted min animation. Note that this does not fire if the animation completes normally.onInterruptParams
An Array of parameters to pass the onInterrupt function. For example,gsap.to(".class", {x:100, onInterrupt:myFunction, onInterruptParams:["param1", "param2"]});
.onRepeat
Function A function that should be called each time the animation repeats.onRepeatParams
Array An Array of parameters to pass theonRepeat
function. For example,gsap.timeline({onRepeat: myFunction, onRepeatParams: ["param1", "param2"]});
.onReverseComplete
Function A function that should be called when the animation has reached its beginning again from the reverse direction. For example, ifreverse()
is called the tween will move back towards its beginning and when itstime
reaches0
,onReverseComplete
will be called. This can also happen if the animation is placed in a timeline instance that gets reversed and plays the animation backwards to (or past) the beginning.onReverseCompleteParams
Array An array of parameters to pass theonReverseComplete
function. For example,gsap.timeline({onReverseComplete: myFunction, onReverseCompleteParams: ["param1", "param2"]});
.onStart
Function A function that should be called when the animation begins (when itstime
changes from0
to some other value which can happen more than once if the tween is restarted multiple times).onStartParams
Array An array of parameters to pass theonStart
function. For example,gsap.timeline({onStart: myFunction, onStartParams: ["param1", "param2"]});
.onUpdate
Function A function that should be called every time the animation updates (on every frame while the animation is active).onUpdateParams
Array An array of parameters to pass theonUpdate
function. For example,gsap.timeline({onUpdate: myFunction, onUpdateParams: ["param1", "param2"]});
.paused
Boolean Iftrue
, the animation will pause itself immediately upon creation.repeat
Number Number of times that the animation should repeat after its first iteration. For example, ifrepeat
is1
, the animation will play a total of twice (the initial play plus 1 repeat). To repeat indefinitely, use-1
.repeat
should always be an integer.repeatDelay
Number Amount of time in seconds between repeats. For example, ifrepeat
is2
andrepeatDelay
is1
, the animation will play initially, then wait for 1 second before it repeats, then play again, then wait 1 second again before doing its final repeat.repeatRefresh
SettingrepeatRefresh: true
causes a repeating timeline toinvalidate()
all of its child tweens and re-record their starting/ending values internally on each full iteration (not including yoyo's). This is useful when you use dynamic values (relative, random, or function-based). For example,x: "random(-100, 100)"
would get a new random x value on each repeat.duration
,delay
, andstagger
do NOT refresh.smoothChildTiming
Boolean Controls whether or not child animations are repositioned automatically (changing theirstartTime
) in order to maintain smooth playback when timing-related properties are changed on-the-fly. For example, imagine that the timeline’s playhead is on a child tween that is 75% complete, moving element’s left from 0 to 100 and then that tween’sreverse()
method is called. IfsmoothChildTiming
isfalse
(the default except for the globalTimeline), the tween would flip in place, keeping itsstartTime
consistent. Therefore the playhead of the timeline would now be at the tween’s 25% completion point instead of 75%. See the "How to timelines work?" section below for details.yoyo
Boolean Iftrue
, every other repeat cycle will run in the opposite direction so that the tween appears to go back and forth (forward then backward). This has no affect on thereversed
property though. So ifrepeat
is2
andyoyo
isfalse
, it will look like: start - 1 - 2 - 3 - 1 - 2 - 3 - 1 - 2 - 3 - end. But ifyoyo
istrue
, it will look like: start - 1 - 2 - 3 - 3 - 2 - 1 - 1 - 2 - 3 - end.
Property
Description
Defaults
Anything in the defaults
object of a timeline gets inherited by its child animations when they get created, so if you find yourself setting the same ease
or duration
(or any value) over and over again, this can help make your code more concise. For example:
// WITHOUT defaults (long)
var tl = gsap.timeline();
tl.to(".class1", { rotation: -270, duration: 1, ease: "elastic" })
.to(".class2", { rotation: -360, duration: 1, ease: "elastic" })
.to(".class3", { rotation: -180, duration: 1, ease: "elastic" });
//WITH defaults (shorter)
var tl = gsap.timeline({ defaults: { duration: 1, ease: "elastic" } });
tl.to(".class1", { rotation: -270 }) //child tweens will inherit the duration and from the parent timeline!
.to(".class2", { rotation: -360 })
.to(".class3", { rotation: -180 });
Any defaults you set this way will get pushed into every child tween - it's not limited to a certain subset of properties. Inherited defaults are easily overwritten anytime a property is declared on a child animation.
Nesting
Nest timelines within timelines as deeply as you want. This lets you modularize your code and make it more maintainable. For example, you could build your animation in sections and stitch them together in a master timeline like:
function intro() {
var tl = gsap.timeline();
//...add animations here...
return tl;
}
function middle() {
var tl = gsap.timeline();
//...add animations here...
return tl;
}
function conclusion() {
var tl = gsap.timeline();
//...add animations here...
return tl;
}
// stitch them together in a master timeline...
var master = gsap.timeline();
master
.add(intro())
.add(middle(), "+=2") //with a gap of 2 seconds
.add(conclusion(), "-=1"); //overlap by 1 second
Other Timeline Features
Speed up or slow down the entire timeline with its
timeScale()
method. You can even tween it to gradually speed up or slow down the animation smoothly!Get or set the progress of the timeline using its
progress()
ortotalProgress()
methods (totalProgress() just includes any repeats). For example, to skip to the halfway point, setmyTimeline.progress(0.5);
.Tween the
time()
,totalTime()
,progress()
, ortotalProgress()
to fast-forward or rewind the timeline. You could even attach a slider to one of these to give the user the ability to drag forward or backward through the timeline.Add
onComplete
,onStart
,onUpdate
,onRepeat
and/oronReverseComplete
callbacks using the constructor'svars
object likevar tl = gsap.timeline({onComplete: myFunction});
.Kill the tweens of a particular object inside the timeline with
killTweensOf(target)
or get the tweens of an object withgetTweensOf()
or get all the tweens and timelines in the timeline withgetChildren()
.Set the timeline to repeat any number of times or indefinitely. You can even set a delay between each repeat cycle and/or cause the repeat cycles to yoyo, appearing to reverse direction every other cycle.
Get the
currentLabel()
or find labels at various positions in the timeline usingnextLabel()
andpreviousLabel()
Sample code:
//create the timeline that repeats 3 times with 1 second between each repeat and then call myFunction() when it completes
var tl = gsap.timeline({ repeat: 3, repeatDelay: 1, onComplete: myFunction });
//add a tween
tl.to(".class", { duration: 1, x: 200, y: 100 });
//add another tween 0.5 seconds after the end of the timeline (makes sequencing easy)
tl.to("#id", { duration: 0.8, opacity: 0 }, "+=0.5");
//reverse anytime
tl.reverse();
//Add a "spin" label 3-seconds into the timeline
tl.addLabel("spin", 3);
//insert a rotation tween at the "spin" label (you could also define the insertion point as the time instead of a label)
tl.to(".class", { duration: 2, rotation: "+=360" }, "spin");
//go to the "spin" label and play the timeline from there
tl.play("spin");
//nest another timeline inside your timeline...
var nested = gsap.timeline();
nested.to(".class2", { duration: 1, x: 200 });
tl.add(nested, "+=3"); //add nested timeline after a 3-second gap
How do timelines work?
Every animation (Tween and Timeline) is placed on a parent Timeline. In a sense, they all have their own playheads (that's what its "time" refers to, or "totalTime" which is identical except that it includes repeats and repeatDelays) and when the parent's playhead moves to a new position, it updates the childrens' too (unless they're paused).
When a timeline renders at a particular time, it loops through its children and says "okay, you should render as if your playhead is at ____" and if that child is a Timeline with children, it does the same to its children, right on down the line. So the playheads generally remain synchronized.
When you unpause an animation (resume()
or play()
), it essentially picks up the playhead and moves it so that its internal playhead is synchronized with wherever the parent's playhead is at that moment, thus things play perfectly smoothly. That is, unless the timeline's smoothChildTiming
is false
in which case that child won't move - its startTime
will remain locked to where it was.
So basically when smoothChildTiming
is true
, the engine will rearrange things on the fly to ensure the playheads line up so that playback feels seamless and smooth. The same thing happens when you reverse()
or alter the timeScale
, etc. - the animation's startTime shifts automatically. But sometimes you might not want that behavior - that's when smoothChildTiming: false
is handy on a parent timeline.
One more example: let's say you've got a 10-second tween that's just sitting on the root timeline and you're 2-seconds into the tween. Let's assume it started at exactly 0 on the root to make this easy, and then when it's at 2-seconds, you do tween.seek(5)
. The playhead of the root isn't affected - it keeps going exactly as it always did, but in order to make that tween jump to 5 seconds and play appropriately, the tween's startTime
gets changed to -3. That way, the tween's playhead and the root playhead are perfectly aligned.
Notes
- You can access GSAP's global timeline via
gsap.globalTimeline
but be careful because if, for example, you pause() or timeScale() it, that affects EVERYTHING including delayedCalls(). You can usegsap.exportRoot()
instead to basically wrap all of the existing animations on the root (optionally excluding delayedCalls) into a new Timeline instance, isolating those from future animations you create. For example, if you have a bunch of animations going on in a game and then the user clicks a button to pop open a modal window that should slow all the game animations to 1/10ths speed...but you want you modal animations to be full-speed, that's the perfect case for exportRoot().
Properties
autoRemoveChildren : Boolean |
data : * |
labels : Object |
parent : Timeline |
smoothChildTiming : Boolean |
vars : Object |
Methods
add( child:[Tween | Timeline | Label | Callback | Array], position:[Number | String | Label] ) : self |
addLabel( label:String, position:[Number | String] ) : self |
addPause( position:[String | Number | Label], callback:Function, params:Array ) : self |
call( callback:Function, params:Array, position:* ) : self |
clear( labels:Boolean ) : self |
currentLabel( value:String ) : [String | self] |
delay( value:Number ) : [Number | self] |
duration( value:Number ) : [Number | self] |
endTime( includeRepeats:Boolean ) : [Number | self] |
eventCallback( type:String, callback:Function, params:Array ) : [Function | self] |
from( target:[ Object | Array | String ], vars:Object, position:[ Number | String ] ) : self |
fromTo( target:[ Object | Array | String ], fromVars:Object, toVars:Object, position:[ Number | String ] ) : self |
getById( id:String ) : Animation |
getChildren( nested:Boolean, tweens:Boolean, timelines:Boolean, ignoreBeforeTime:Number ) : Array |
getTweensOf( target:[Object | Selector text | Array], nested:Boolean ) : Array |
globalTime( localTime:Number ) : Number |
invalidate( ) : self |
isActive( ) : Boolean |
iteration( value:Number ) : [Number | self] |
kill( ) : Timeline |
killTweensOf( targets:Selector text | Array | Object, props:String, onlyActive:Boolean ) : Timeline |
nextLabel( time:Number ) : String |
pause( atTime:*, suppressEvents:Boolean ) : self |
paused( value:Boolean ) : [Boolean | self] |
play( from:*, suppressEvents:Boolean ) : self |
previousLabel( time:Number ) : String |
progress( value:Number, suppressEvents:Boolean ) : [Number | self] |
recent( ) : [Tween | Timeline | Callback] |
remove( value:[Tween | Timeline | Callback | Label] ) : self |
removeLabel( label:String ) : self |
removePause( position:[Number | Label] ) : self |
repeat( value:Number ) : [Number | self] |
repeatDelay( value:Number ) : [Number | self] |
restart( includeDelay:Boolean, suppressEvents:Boolean ) : self |
resume( ) : self |
reverse( from:*, suppressEvents:Boolean ) : self |
reversed( value:Boolean ) : [Boolean | self] |
revert( ) : Self |
seek( position:*, suppressEvents:Boolean ) : self |
set( target:[ Object | Array | String ], vars:Object, position:[ Number | String ] ) : self |
shiftChildren( amount:Number, adjustLabels:Boolean, ignoreBeforeTime:Number ) : self |
startTime( value:Number ) : [Number | self] |
then( callback:Function ) : Promise |
time( value:Number, suppressEvents:Boolean ) : [Number | self] |
timeScale( value:Number ) : [Number | self] |
to( target:[ Object | Array | String ], vars:Object, position:[ Number | String ] ) : self |
totalDuration( value:Number ) : [Number | self] |
totalProgress( value:Number, suppressEvents:Boolean ) : [Number | self] |
totalTime( time:Number, suppressEvents:Boolean ) : [Number | self] |
tweenFromTo( fromPosition:[Number | Label], toPosition:[Number | Label], vars:Object ) : Tween |
tweenTo( position:[Number | Label], vars:Object ) : Tween |
yoyo( value:Boolean ) : [Boolean | self] |