Creative Coding with SVGs
zuubaDigital

Timelines

codepen practice page

What is a Timeline?

The GSAP timeline is a powerful tool that allows you to create detailed, sequenced animations. Using "traditional" methods it can be difficult to create and magage manage complex animations involving multiple elements, but GSAP timelines makes the process simple and intuitive. In this lesson we're going to create the relativly simple animation below, but know that with timelines the only limit is your imagination!

TEXTANIMATION

Recreating the example

I've created a starter page on codpen that contains all of the visual elements that we'll need to recreate the animation above here..

Elements that we'll be animating

There are essentially three elements that we'll be animating

1.The black circle with an id of circleHolder that starts off small, animates to the center of the svg and then expands to fill it. Our circle positioned at the cebter of the svg and has a radius of 400:

1 2 3 <g id="circleHolder" transform="translate(250 250)"> <circle cx="0" cy="0" r="400" fill="black" stroke="none" /> </g>

2.A white line with an id of lineHolder that extends across the middle of the svg after the black circle fills the screen:

1 2 3 <g id="lineHolder" transform="translate(250 250) scale(1 1)"> <path d="M-250,0 h500" stroke="white" stroke-width="2" /> </g>

3.And the individual text elements for each letter in "TEXT ANIMATION", each with a class of "letter". All the text elements are inside a group with the id of textHolder.

1 2 3 4 5 6 <g id="textHolder"> <text x="90" y="230" text-anchor="middle" dominant-baseline="middle" class="letter">T</text> <text x="115" y="230" text-anchor="middle" dominant-baseline="middle" class="letter">E</text> <text x="140" y="230" text-anchor="middle" dominant-baseline="middle" class="letter">X</text> // other letters ... </g>

Creating the timeline object

The first thing we need to do is create our timeline object.

gsap.timeline( {vars} );

In the animate method let's put the following:

1 2 3 function animate(){ theTween = gsap.timeline(); }

You can configure your timeline object by passing it a vars object that allows you to set various options. For example, we want our animation to yoyo and repeat, so we'll be including those properties in the vars object. Additionally, we'll add a repeatDelay of 1 second to introduce a slight delay between each repetition.

1 2 3 function animate(){ theTween = gsap.timeline({repeat: -1, yoyo:true, repeatDelay: 1}); }

The first part of our animation will be the circle animating down fron "off screen" to the center of the svg, so we'll need to add a fromTo animation to our timeline object. It will look something like this

theTween.fromTo(...)
1 2 3 4 5 6 theTween = gsap.timeline({repeat: -1, yoyo:true, repeatDelay: 1}) theTween.fromTo( "#circleHolder", { attr: { transform: "translate(250 -30) scale(.03)"} }, { attr: { transform: "translate(250 250) scale(.03)"}, duration: dur, ease:"back"} )

So in the from part of the animation I set the initial translate y position of the circle to -30, and set it's scale to 0.03 so that it looks like a little dot. In the to part of the animation I move it to the center of the svg (250,20), keeping it's scale at 0.03.

In the next phase of the animation we'll be tweening the scale of the circle from .03 to it's full size (scale:1) so that it covers the entire svg. To do so we simply "chain" another fromTo animation to our current animation:

theTween.fromTo(...).fromTo(...)
1 2 3 4 5 6 7 8 9 theTween = gsap.timeline({repeat: -1, yoyo:true, repeatDelay: 1}); theTween.fromTo( "#circleHolder", { attr: { transform: "translate(250 -30) scale(.03)"} }, { attr: { transform: "translate(250 250) scale(.03)"}, duration: dur, ease:"back"}) .fromTo( "#circleHolder", { attr: { transform: "scale(.03)"} }, { attr: { transform: "scale(1)"}, duration: dur})

You might notice a problem. The circle animates properly to the center, but when it expands it moves to the upper right hand corner, instead of expandig from the center of the svg. Why?

TEXTANIMATION

The reason: whenever you use GSAP to animate the transform attribute, you need to explicitly set the translate location. If not, gsap will simply assume a translate position of (0 0). Let's make the correction:

1 2 3 4 5 6 7 8 9 theTween = gsap.timeline({repeat: -1, yoyo:true, repeatDelay: 1}); theTween.fromTo( "#circleHolder", { attr: { transform: "translate(250 -30) scale(.03)"} }, { attr: { transform: "translate(250 250) scale(.03)"}, duration: dur, ease:"back"}) .fromTo( "#circleHolder", { attr: { transform: "translate(250 250) scale(.03)"} }, { attr: { transform: "translate(250 250) scale(1)"}, duration: dur})
TEXTANIMATION

Now let's animate the line. Since our 500px line is already centered inside the lineHolder container, all we need to do is animate the lineHolder element's scale from 0 to 1. Again, we need to make sure that we include the translate position of (250 250), so that the line expends from the center of our SVG.

1 2 3 4 5 6 7 8 9 10 11 12 13 theTween = gsap.timeline({repeat: -1, yoyo:true, repeatDelay: 1}); theTween.fromTo( "#circleHolder", { attr: { transform: "translate(250 -30) scale(.03)"} }, { attr: { transform: "translate(250 250) scale(.03)"}, duration: dur, ease:"back"}) .fromTo( "#circleHolder", { attr: { transform: "translate(250 250) scale(.03)"} }, { attr: { transform: "translate(250 250) scale(1)"}, duration: dur}) .fromTo( "#lineHolder", { attr: { transform: "translate(250 250) scale(0 1)"} }, { attr: { transform: "translate(250 250) scale(1 1)"}, duration: dur})

Now let's animate the text. This is what our animation currently looks like after the line has finished animating.

TEXTANIMATION

We want the text to "pop up" over the line once it finishes scaling. We can use the technique we learned last lesson of passing a class name (in this case letter) to the fromTo method and adding a stagger.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 theTween = gsap.timeline({repeat: -1, yoyo:true, repeatDelay: 1}); theTween.fromTo( "#circleHolder", { attr: { transform: "translate(250 -30) scale(.03)"} }, { attr: { transform: "translate(250 250) scale(.03)"}, duration: dur, ease:"back"} ).fromTo( "#circleHolder", { attr: { transform: "translate(250 250) scale(.03)"} }, { attr: { transform: "translate(250 250) scale(1)"}, duration: dur} ).fromTo( "#lineHolder", { attr: { transform: "translate(250 250) scale(0 1)"} }, { attr: { transform: "translate(250 250) scale(1 1)"}, duration: dur} ).fromTo( ".letter", { attr: { y:100} }, { attr: { y:0 }, duration: dur, stagger:.1, ease:"back"} );
TEXTANIMATION

Now the only thing remaining is to make the letters look like they're popping up from the line. To do this we'll define a clipPath and then add it to the textHolder (the group that contains all the letters). As you recall from the chapter on reusable elements, a clipPath defines a rectangular area and restricts the visibility of elements to within that specified area, effectively hiding any parts of the elements that fall outside this area. Our clipPath will only allow the text elements to be visible when they animate above the line. Our clipPath will simply be a rectangle that covers the top half of our SVG.

We'll define our clipPath in the defs section, and then apply it to the texHolder (which contains all the letters).

1 2 3 4 5 6 7 <defs> <clipPath id="text-clip-path"> <rect x="0" y="0" width="500" height="250" /> </clipPath> </defs> ... <g id="textHolder" clip-path="url(#text-clip-path)">

And that's it! You can find the final code here..