Vue.js 3 GreenSock (GSAP) Animating Tutorial

In this Vue tutorial we learn how to use the Greensock (GSAP) Javascript animation library for easy animations.

We cover how to install GSAP, the methods it uses to animate, easing, staggering and how to use lifecycle hooks instead of the transition component.

Lesson Video

If you prefer to learn visually, you can watch this lesson in video format.

Lesson Project

If you want to follow along with the examples, you will need to create an app generated by the Vue CLI .

What is GreenSock (GSAP)?

GSAP is a Javascript animation library that helps developers script performant and engaging animations.

GSAP is fast, robust with features and handles browser compatibility for us by default. And because it’s framework-agnostic, we can easily integrate and use it with Vue.

How to install GSAP

To install the GSAP package, open a new terminal and navigate to your project folder, then run the following command.

tip If you’re working in VSCode, you can go to Terminal > New Terminal to open a terminal or command prompt with the project folder already selected.

Command: npm install gsap
npm install gsap

Once the package has been installed, we should see it in the package.json file under “dependencies”.

Alternatively, you can use Yarn to install the package.

Command: yarn add gsap
yarn add gsap

As with any other dependency, we need to import GSAP into the file or component where we want to use it.

Example: import gsap
import gsap from 'gsap'

GSAP Methods

GSAP provides us with multiple methods to create animations.

Following are the most common methods.

  • gsap.to allows us to define the ending values. In other words, where we want to animate to and how it should happen.
  • gsap.from allows us to define the starting values. In other words, a backwards animation.
  • gsap.fromTo allows us to define both the start and end values.

These methods take two parameters.

  1. The element that we want to animate.
  2. A vars object containing the properties to animate as well as animation options that specify the duration, easing functionality etc.
Syntax: gsap method
gsap.method(element, {
  property: value,
  duration: 1,
})

GSAP uses short codes for 2D and 3D transform-related properties.

CSSGSAP
translateX(100px)x: 100
translateY(100px)y: 100
rotate(360deg)rotation: 360
rotateX(360deg)rotationX: 360
rotateY(360deg)rotationY: 360
skewX(45deg)skewX: 45
skewY(45deg)skewY: 45
scale(2, 2)scale: 2
scaleX(2)scaleX: 2
scaleY(2)scaleY: 2
translateX(-50%)xPercent: -50
translateY(-50%)yPercent: -50

To demonstrate, let’s set up a heading in the transition component with the before-enter and enter hooks.

In the beforeEnter method, we’ll set the starting state of the heading. In other words, where the element will be when it starts the animation. In the enter method, we’ll use the gsap.to method and set the end state of the animation with a duration.

Example: src/App.vue
<template>
  <transition
    appear
    @before-enter="beforeEnter"
    @enter="enter"
  >
    <h1>GreenSock Animation Library</h1>
  </transition>
</template>

<script>
import gsap from 'gsap'

export default {
  methods: {
    // where the animation will start from
    beforeEnter(el) {
      el.style.opacity = '0'
      el.style.transform = 'translateY(-100px)'
    },
    // where the animation will end up
    enter(el) {
      gsap.to(el, {
        duration: 1,
        y: 0,
        opacity: 1,
      })
    }
  }
}
</script>

<style>
#app {text-align:center}
</style>

If we run the example in the browser, the heading will slide down from the top of the page.

How to delay the start of an animation

GSAP allows us to specify a delay before the animation should begin with the delay property.

Syntax: delay
gsap.method(element, {
  // delay in seconds
  delay: 1
})

To demonstrate, let’s add a 2 second delay to our example.

Example: src/App.vue
<template>
  <transition
    appear
    @before-enter="beforeEnter"
    @enter="enter"
  >
    <h1>GreenSock Animation Library</h1>
  </transition>
</template>

<script>
import gsap from 'gsap'

export default {
  methods: {
    beforeEnter(el) {
      el.style.opacity = '0'
      el.style.transform = 'translateY(-100px)'
    },
    enter(el) {
      gsap.to(el, {
        delay: 2,
        duration: 1,
        y: 0,
        opacity: 1,
      })
    }
  }
}
</script>

<style>
#app {text-align:center}
</style>

If we run the example in the browser, the heading will slide down into the page after 2 seconds.

How to repeat an animation

We can repeat an animation by specifying the number of times we want it to repeat in the repeat property of the vars object.

Syntax: repeat
gsap.method(element, {
  repeat: 1
})

note The number of repetitions is added to the initial animation. A value of 1 would mean the animation happens once, then repeat once for a total of 2.

tip To infinitely repeat the animation, we can use -1 as the value.

To demonstrate, let’s add an infinite repeat to our animation. We’ll also remove the opacity and delay for now.

Example: src/App.vue
<template>
  <transition
    appear
    @before-enter="beforeEnter"
    @enter="enter"
  >
    <h1>GreenSock Animation Library</h1>
  </transition>
</template>

<script>
import gsap from 'gsap'

export default {
  methods: {
    beforeEnter(el) {
      el.style.transform = 'translateY(-100px)'
    },
    enter(el) {
      gsap.to(el, {
        duration: 1.7,
        repeat: -1,
        y: 0
      })
    }
  }
}
</script>

<style>
#app {text-align:center}
</style>

If we run the example in the browser, the heading will slide down from the top and restart the animation once it reaches its end position.

GSAP easing functions

GSAP provides us with a lot of easing functions that we can use as a string value in the ease property of the vars object.

We can select one from the list in the documentation to see a visual representation of how it will animate and also choose an easing type.

There are three types of easings available.

  1. in will start slowly, then go faster toward the end of the animation.
  2. out will start fast, then slow down toward the end of the animation.
  3. inOut wil be fast through the middle, but start and end slowly.
Syntax: easing
gsap.method(element, {
  ease: 'easing.type'
})

By default, GSAP uses the power1.out ease if we don’t specify our own.

To demonstrate, let’s add the bounce animation with a type of out to our example.

Example: src/App.vue
<template>
  <transition
    appear
    @before-enter="beforeEnter"
    @enter="enter"
  >
    <h1>GreenSock Animation Library</h1>
  </transition>
</template>

<script>
import gsap from 'gsap'

export default {
  methods: {
    beforeEnter(el) {
      el.style.transform = 'translateY(-100px)'
    },
    enter(el) {
      gsap.to(el, {
        duration: 1.7,
        ease: 'bounce.out',
        y: 0
      })
    }
  }
}
</script>

<style>
#app {text-align:center}
</style>

If we run the example in the browser, the heading will slide in from the top and bounce when it reaches its end position.

How to create a stagger effect

GSAP makes it easy for us to create a stagger effect with the stagger property of the vars object. Its value is the delay in seconds between the elements.

Syntax: stagger
gsap.method(element, {
  // delay in seconds
  stagger: .1
})

To demonstrate, we’ll create some cards and iterate through them with a v-for based on id data properties. This time, we’ll use the from animation method and add the stagger property with a .1 second delay.

tip Because the cards are in a container, we can use the card’s class to animate each element separately.

Example: src/App.vue
<template>
  <transition appear @enter="enter">
    <div class="container">
      <div class="card" v-for="card in cards" :key="card.id"></div>
    </div>
  </transition>
</template>

<script>
import gsap from 'gsap'

export default {
  data() {
    return { cards: [1, 2, 3, 4, 5, 6] }
  },
  methods: {
    enter() {
      gsap.from('.card', {
        repeat: -1,
        duration: .7,
        stagger: .1,
        y: -150
      })
    }
  }
}
</script>

<style>
.container{display:flex;flex-wrap:wrap;justify-content:space-evenly;width:400px;margin:20px auto}
.card{width:50px;height:50px;background:dodgerblue}
</style>

If we run the example in the browser, the cards will slide down into the page one after another.

How to indicate a completed animation

Vue doesn’t know when a GSAP animation will complete. This can cause problems with our animations because transition hooks may fire when they’re not supposed to.

To demonstrate, we’ll add the after-enter hook to our example and log a message to the console when it triggers. We’ll also increase the duration and stagger delay of the animation to make it clear what’s happening.

Example: src/App.vue
<template>
  <transition
    appear
    @enter="enter"
    @after-enter="afterEnter"
  >
    <div class="container">
      <div class="card" v-for="card in cards" :key="card.id"></div>
    </div>
  </transition>
</template>

<script>
import gsap from 'gsap'

export default {
  data() {
    return { cards: [1, 2, 3, 4, 5, 6] }
  },
  methods: {
    enter() {
      gsap.from('.card', {
        repeat: -1,
        duration: 7,
        stagger: 1,
        y: -150
      })
    },
    afterEnter() {
      console.log('after enter')
    }
  }
}
</script>

<style>
.container{display:flex;flex-wrap:wrap;justify-content:space-evenly;width:400px;margin:20px auto}
.card{width:50px;height:50px;background:dodgerblue}
</style>

When we run the example in the browser, the console message will show before the animation completes. This would be true for all the leaving hooks as well.

If we had any following animations, the whole thing would break.

Luckily, we can tell GSAP when an animation completes with the onComplete property. Vue also gives us a second parameter in the transition hook that we can pass to the GSAP property.

note We must add the first parameter to the hook, even if we don’t use it.

Syntax: onComplete and done function
hook(el, done) {
  gsap.method(element, {
    onComplete: done
  })
}

To demonstrate, let’s add it to our example. We’ll remove the repeat property otherwise the after-enter hook will never be executed and we won’t see the console message.

Example: src/App.vue
<template>
  <transition
    appear
    @enter="enter"
    @after-enter="afterEnter"
  >
    <div class="container">
      <div class="card" v-for="card in cards" :key="card.id"></div>
    </div>
  </transition>
</template>

<script>
import gsap from 'gsap'

export default {
  data() {
    return { cards: [1, 2, 3, 4, 5, 6] }
  },
  methods: {
    enter(el, done) {
      gsap.from('.card', {
        duration: 7,
        stagger: 1,
        y: -150,
        onComplete: done
      })
    },
    afterEnter() {
      console.log('after enter')
    }
  }
}
</script>

<style>
.container{display:flex;flex-wrap:wrap;justify-content:space-evenly;width:400px;margin:20px auto}
.card{width:50px;height:50px;background:dodgerblue}
</style>

This time, the message will be printed to the console only after the animation is complete.

Animations in lifecycle hooks instead of the transition component

We can execute our animations inside a component’s lifecycle hooks . In that case, we don’t need to use the transition component or its hooks.

To demonstrate, let’s remove the transition component from our example and move the GSAP animation over to the mounted lifecycle hook.

Example: src/App.vue
<template>
  <div class="container">
    <div class="card" v-for="card in cards" :key="card.id"></div>
  </div>
</template>

<script>
import gsap from 'gsap'

export default {
  data() {
    return { cards: [1, 2, 3, 4, 5, 6] }
  },
  mounted() {
    gsap.from('.card', {
      repeat: -1,
      duration: .7,
      stagger: .1,
      y: -150
    })
  }
}
</script>

<style>
.container{display:flex;flex-wrap:wrap;justify-content:space-evenly;width:400px;margin:20px auto}
.card{width:50px;height:50px;background:dodgerblue}
</style>

If we run the example in the browser, everything still works as expected.

Further Reading

For more information on the topics covered in this lesson, please see the relevant sections below.