Image

animating-vue

7 min read
Last update: December 19, 2021

Timelines with GSAP

When working with GSAP, often our animations start to increase in complexity. There comes a point where a simple tween isn’t sufficient and we need to create multiple animations that work together in a sequence. For this, we can use GSAP’s timeline feature, which allows us to chain a group of tweens together to create more complex animations that work together as one.


Our first Timeline

Let’s start by taking at the Timeline.vue component in our starting code.

📃 src/views/Timeline.vue

<template>
    <div>
        <img class="runner first" src="../assets/runner.png" alt="runner" />
        <img class="runner second" src="../assets/runner.png" alt="runner" />
        <img class="runner third" src="../assets/runner.png" alt="runner" />
    </div>
</template>

<script>
    import gsap from 'gsap'

    export default {
        mounted() {
            // timeline will go here
        }
    }
</script>

<style scoped>
    .runner {
        display: block;
        height: 5em;
        width: 5em;
        margin-top: 1.5em;
    }
</style>

In the template, we have three images of runners. In a moment, we are going to make each of them “race” to the right side of the browser. Since we already have gsap imported into this component, we just need to initialize our timeline, which we’ll do in our mounted hook.

📃 src/views/Timeline.vue

mounted() {
let tl = gsap.timeline()
}

Here, we’ll also add a tween for each runner onto the timeline. Let’s start with the first runner:

📃 src/views/Timeline.vue

mounted() {
let tl = gsap.timeline()
tl.to('.first', { x: 700, duration: 2, ease: 'expo.out' })
}

Here, we’re using the to method like we’ve used in previous lessons. This method allows us to create a tween and apply it to the target (the element we’re animating). In this case, we are animating the element with the class name of '.first'. We’re telling that element to “run” 700 pixels to the right, over a duration of 2 seconds, and giving that motion an ease called 'expo.out'.

If we check this out in the browser, we’ll see that runner “race” 700px over to the right. So far so good, now let’s create two more to tweens, one for each of the remaining runners.

📃 src/views/Timeline.vue

mounted() {
 ...
 tl.to('.first', { x: 200, duration: 2, ease: 'expo.out' })
 tl.to('.second', { x: 400, duration: 2, ease: 'expo.out' }, 0.5) // now with an absolute position
  tl.to('.third', { x: 600, duration: 2, ease: 'expo.out' })
}

Now, when this component is mounted, each of our runners “race” 700 pixels to the right. As it stands, our runners “take off” one after the other. After the first runner reaches its destination, the second runner goes, then the third after that. However, what if we wanted our runners to take off at the same time? Or what if we wanted the second runner to start a half second after the first runner goes?


Position Parameter

We can alter the timing that animations within our timeline fire by using the the position parameter. This parameter lets us define when a tween occurs within the timeline’s sequence, and there are several ways for us to set that position.

Absolute Position

One way to set the position of a tween within a timeline is by setting its absolution position. This means we provide an integer value, which represents the value of seconds from the start of the animation. So if we wanted an animation to fire a half second into the timeline, we’d set that animation’s position value as 0.5, like so:

📃 src/views/Timeline.vue

mounted() {
 ...
 tl.to('.first', { x: 200, duration: 2, ease: 'expo.out' })
 tl.to('.second', { x: 400, duration: 2, ease: 'expo.out' }, '-=.75')
  tl.to('.third', { x: 600, duration: 2, ease: 'expo.out' })
}

Now, the second runner will start “running” 0.5 seconds into the sequence.

Relative Position

Sometimes it may be more useful for us if the position of our animations are set in relation to other animations. To set a relative position, we use a string like '-=.75' or +=.75 to set the start of one tween .75 seconds before (-=) or after (+=) the end of the animation before it. So let’s add a relative position to the second tween in our timeline.

📃 src/views/Timeline.vue

mounted() {
 ...
 tl.to('.first', { x: 200, duration: 2, ease: 'expo.out' })
 tl.to('.second', { x: 400, duration: 2, ease: 'expo.out' }, '-=2')
  tl.to('.third', { x: 600, duration: 2, ease: 'expo.out' })
}

https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2F1576524372145_timelines.001.opt.jpg?alt=media&token=53ed2627-9b9f-400a-98c6-58e8d2a57b0d

Now the second animation will start .75 seconds before the first one ends.


Start/End Position

If we wanted our second animation to start at the same time as our first, you might think we would change the second animation’s position parameter to -=2.

📃 src/views/Timeline.vue

This would work for now, but what if we need to change the duration of our first animation later on? For example, if the first animation’s duration changed to 4, then our second animation’s position would only move back halfway toward the start of our first because 4-2=2. If we want the first and second tweens to fire at the same time regardless of how long our first runner’s duration is, we can achieve that by using a third way of setting position: < When we set the position of our second runner as '<', that tells it to always start when the first animation starts. Alternatively, '>' tells it to start when the first animation ends.

📃 src/views/Timeline.vue

mounted() {
 ...
 tl.to('.first', { x: 200, duration: 2, ease: 'expo.out' })
 tl.to('.second', { x: 400, duration: 2, ease: 'expo.out' }, '<') // starts when first tween starts
  tl.to('.third', { x: 600, duration: 2, ease: 'expo.out' },  '<') // starts when second tween starts, which is when first tween starts
}

In the code above, all three runners will start at the same time. Why? Well, we’re telling our third runner to start when the second runner does, and the second runner starts when the first runner does.

https://firebasestorage.googleapis.com/v0/b/vue-mastery.appspot.com/o/flamelink%2Fmedia%2F1576524376652_timelines.002.opt.jpg?alt=media&token=fd670385-7432-4e19-9e7f-66d3cf9c5f8f

We can also add to these to make them relative. For example, what do you think a position of '0.5' would do?

📃 src/views/Timeline.vue

mounted() {
 ...
 tl.to('.first', { x: 200, duration: 2, ease: 'expo.out' })
 tl.to('.second', { x: 400, duration: 2, ease: 'expo.out' }, '<0.5') // What will this do?
  tl.to('.third', { x: 600, duration: 2, ease: 'expo.out' }) 
}

If you guessed that the second animation would fire a half second after the start of the first, taking the third along with it, then you’re correct.

As you can see, there are a lot of different ways to position animations within a timeline, including even more that you can check out here.


Timeline Duration

With a simple timeline like this, we can quite easily count up the timeline’s total duration. However, in more complex timelines, we can run tl.duration() and that would add up all of our tweens’ individual durations and return our timeline’s total duration for us. This may be helpful when deciding where to position the tweens within your timeline’s sequence.


Repeating / Looping Timelines

You may have the need to repeat or even loop a timeline. We can easily achieve that by passing in an object when creating our timeline, like so:

gsap.timeline({ repeat: 2 })

Now, our timeline will repeat itself twice. If we wanted it to repeat indefinitely on a loop, we’d use -1.

gsap.timeline({ repeat: -1 })

There’s also the ability to add a delay before the next repetition.

gsap.timeline({ repeat: -1, repeatDelay: 1 })

Now this timeline will wait 1 second before starting over.


Let’s ReVue

In this lesson, we saw how gsap timeline’s allows us to sequence multiple animations, adding or removing space between them with the position parameter. In the next and final lesson of the course, we’ll see how we can create more flexible and scalable animations by using nested timelines.