Web Development

Vue Transitions and Animations

May 7th, 2020 | By John Au-Yeung | 11 min read

Vue transitions and animations help bring web apps to life. Look at some basic animation features built into Vue.js.

With Vue, we can create animations for actions like adding and removing items from lists or toggling an element on and off.

Like many JavaScript front-end frameworks, having the ability to display transition effects for various UI actions is a core feature of Vue.

Basic Animation

We can create basic transitions by defining the CSS class that Vue looks for to create the transition effect. Then we apply the style we want to it.

We will use several classes in Vue to style the transition effect, namely:

  • v-enter: Start state to enter. This class is applied before the element is inserted and removed one frame after the element is applied.

  • v-enter-active: The class that's applied before the element is inserted into the DOM. This class is removed when the transition or animation finishes. It can also be used to define the duration, delay, and easing curve for the entering transition. The easing is the rate of change of the element being animated.

  • v-enter-to: The class for the ending state for entering. It's added one frame after the element is inserted, which is the same time that the v-enter class is removed.

  • v-leave: This class is applied when the element starts leaving the DOM and when the leaving transition is triggered. The class is removed after one frame.

  • v-leave-active: This represents the active state for the leaving transition. It's applied during the whole leaving phase. It's added immediately when the leave transition is triggered and removed when the transition finishes. Use this class to define the duration, delay, and easing for the leaving transition.

  • v-leave-to: The ending state for the leave transition. This class is applied one frame after the leaving transition is triggered, which is the same time that the v-leave class is removed. This class is removed from the element being animated when the transition or animation is done.


The prefix v- is the one we can replace with the name of our animation, and we set it as the value of the name prop of the transition component, which use to add animation to our app.

For instance, we can create a simple transition effect displayed when we toggle an element on and off by writing the following code:

index.js

new Vue({
  el: "#app",
  data: {
    show: true
  }
});
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
      .fade-enter-active,
      .fade-leave-active {
        transition: opacity 0.3s;
      }
      .fade-enter,
      .fade-leave-to {
        opacity: 0;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>
      <transition name="fade">
        <p v-if="show">foo</p>
      </transition>
    </div>
    <script src="index.js"></script>
  </body>
</html>


The animation code is all in index.html. We added the Vue transition component built into the library.

In the same file, we have the CSS defined as follows:

.fade-enter-active,
.fade-leave-active {
    transition: opacity 0.3s;
}
.fade-enter,
.fade-leave-to {
    opacity: 0;
}


In the code above, we have the fade prefix in each of our classes, which corresponds with the value of the name prop set in the transition component.

In each class, we defined the styling that Vue looks for, as we outlined above.

Then, when we click the Toggle button, we see that the word 'foo' will have a fading effect as it's being toggled on and off.

We can change the timing of the transition by adding easing as follows:

index.html

...
    <style>
      .fade-enter-active,
      .fade-leave-active {
        transition: opacity 1s cubic-bezier(1, 0.2, 0.8, 1);
      }
      ...
    </style>
...


In the code above, we have cubic-bezier (1, 0.2, 0.8, 1), which sets the rate of change of the opacity from the coordinates (1, 0.2) and (0.8, 1) by the cubic bezier curve directory.

The curve looks like the one outlined in the Mozilla docs; we only have different coordinates.

We have eased our code to have a variable rate of change in the opacity. This results in transition effects that are more interesting than simple linear transitions.

Scaling Effects

In addition to fading effects, we can change our transition to resize our element instead of having a fading effect.

To make an effect that skews the p element, we can create an animation with the following example:

index.js

new Vue({
  el: "#app",
  data: {
    show: true,
    timer: undefined
  },
  beforeMount() {
    this.timer = setInterval(() => {
      this.show = !this.show;
    }, 1000);
  },
  beforeDestroy() {
    clearInterval(this.timer);
  }
});


index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
      p {
        font-size: 30px;
      }
      .scale-enter-active {
        animation: bounce-in 1s reverse;
      }
      .scale-leave-active {
        animation: bounce-in 1s;
      }
      @keyframes bounce-in {
        0% {
          transform: skewY(0deg);
        }
        25% {
          transform: skewY(2deg);
        }
        50% {
          transform: skewY(-5deg);
        }
        75% {
          transform: skewY(0deg);
        }
        100% {
          transform: skewY(-120deg);
        }
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>
      <transition name="scale">
        <p v-if="show">foo</p>
      </transition>
    </div>
    <script src="index.js"></script>
  </body>
</html>


index.js is the same as the previous example.

In the code above, we have the bounce-in animation that transforms the p element by changing its angle with skewY.

We apply the transition every second with the setInterval callback that toggles this.show the value between true and false after 1 second.

Custom Transition Classes

We can set our own transition class by passing in a few props to the transition component. They're the following:

  • enter-class

  • enter-active-class

  • enter-to-class (available since 2.1.8+)

  • leave-class

  • leave-active-class

  • leave-to-class (available since 2.1.8+)


They correspond to the transition stages that are outlined above.

We can use them as follows:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
      .enter,
      .leave {
        animation: bounce-in 0.5s;
      }
      .active,
      .leave-active {
        animation: bounce-in 0.5s reverse;
      }
      @keyframes bounce-in {
        0% {
          transform: skewY(0deg);
        }
        50% {
          transform: skewY(70deg);
        }
        100% {
          transform: skewY(150deg);
        }
      }
    </style>
  </head>
  <body>
    <div id="app">
      <transition
        enter-class="enter"
        enter-active-class="active"
        leave-class="leave"
        leave-active-class="leave-active"
      >
        <p v-if="show">foo</p>
      </transition>
    </div>
    <script src="index.js"></script>
  </body>
</html>


index.js is the same as the previous example.

In the code above, we specified our own class names for the transition stages.

We specified the class names as the value of the props, and then we used them in the style tag by specifying them with the animation.

Transition Durations

We can specify the duration prop to specify the duration of the transition effect.

For instance, we can use that as follows:

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
      .fade-enter-active,
      .fade-leave-active {
        transition: all 2s;
        transition-timing: ease-in-out;
      }
      .fade-enter,
      .fade-leave-to {
        opacity: 0;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <transition name="fade" :duration="2000">
        <p v-if="show">foo</p>
      </transition>
    </div>
    <script src="index.js"></script>
  </body>
</html>


index.js is the same as the previous example.

In the code above, we have :duration="2000" to make the transition duration 2s.

We can also specify the duration for the enter and leave phases separately by passing in an object as follows:

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
      .fade-enter-active,
      .fade-leave-active {
        transition: opacity 1s cubic-bezier(1, 0.2, 0.8, 1);
      }
      .fade-enter,
      .fade-leave-to {
        opacity: 0;
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>
      <transition name="fade" :duration="{ enter: 800, leave: 500 }">
        <p v-if="show">foo</p>
      </transition>
    </div>
    <script src="index.js"></script>
  </body>
</html>


We have :duration="{ enter: 800, leave: 500 }" to specify the duration for the entering phase as 0.8s and the left phase as 0.5s, respectively.

JavaScript Animations and Transition Hooks

We can attach event listeners to watch events that are emitted by various phases of the transition.

In each hook, we can get the element being animated with the el parameter of each hook.

The enter and leave hooks also have the done function that can be called to end the animation. The done function is needed to be called by JavaScript animations and is optional with CSS animations. Otherwise, the animation will be run synchronously.

These hooks are used mainly for creating JavaScript animations. We don't need them for CSS hooks.

We can create animations for Vue apps with the Velocity library.

index.js

new Vue({
  el: "#app",
  data: {
    show: true
  },
  methods: {
    beforeEnter(el) {
      el.style.opacity = 0;
      el.style.transformOrigin = "left";
    },

    enter(el, done) {
      Velocity(el, { opacity: 1, fontSize: "2.5em" }, { duration: 1300 });
      Velocity(el, { fontSize: "1em" }, { complete: done });
    },
    leave(el, done) {
      Velocity(
        el,
        { translateX: "25px", rotateZ: "270deg" },
        { duration: 1200 }
      );
      Velocity(el, { rotateZ: "220deg" }, { loop: 2 });
      Velocity(
        el,
        {
          rotateZ: "75deg",
          translateY: "50px",
          translateX: "30px",
          opacity: 0
        },
        { complete: done }
      );
    }
  }
});


index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/velocity/1.2.3/velocity.min.js"></script>
  </head>
  <body>
    <div id="app">
      <button @click="show = !show">
        Toggle
      </button>
      <transition
        @before-enter="beforeEnter"
        @enter="enter"
        @leave="leave"
        :css="false"
      >
        <p v-if="show">foo</p>
      </transition>
    </div>
    <script src="index.js"></script>
  </body>
</html>


In the code above, we have the Velocity library so that we can create JavaScript animations with the DOM object that's in the parameter.

We styled the p element in the beforeEnter hook to style it when the transition starts.

We set the p element's opacity to 0 and the base placement of the element with the transformOrigin property to 0.

In the enter hook, we set the opacity and the font size when the p element is being inserted into the DOM. We also add our styling to the p element when it's removed from the DOM.

We added some rotation effects and some font size changes to make our p element look more interesting when clicking on Toggle to remove the p element when it's present in the DOM.

The done function that's in the parameter for enter and leave methods is used as a callback for when the animation is complete.

List Transitions

To add transitions for v-if, we can add transitions for when an item that's being rendered by v-for is being added and removed from the DOM.

Instead of the transition component, we use the transition-group component to apply the transition effect.

For instance, we can create a transition effect to show an animation when items are being added and removed from a list as follows:

index.js

new Vue({
  el: "#app",
  data: {
    items: [1, 2, 3, 4, 5]
  },
  methods: {
    randomIndex() {
      return Math.floor(Math.random() * this.items.length);
    },
    add() {
      this.items = [...this.items, ++this.items[this.items.length - 1]];
    },
    remove() {
      this.items.splice(this.items.length - 1, 1);
    }
  }
});


index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <style>
      .list-enter-active,
      .list-leave-active {
        transition: all 1s;
      }
      .list-enter,
      .list-leave-to {
        opacity: 0;
        transform: translateY(40px);
      }
    </style>
  </head>
  <body>
    <div id="app">
      <button @click="add">Add</button>
      <button @click="remove">Remove</button>
      <transition-group name="list" tag="p">
        <span v-for="item in items" :key="item">
          {{ item }}
        </span>
      </transition-group>
    </div>
    <script src="index.js"></script>
  </body>
</html>


In the code above, we defined the styling for the transition phases as follows:

.list-enter-active,
.list-leave-active {
    transition: all 1s;
}
.list-enter,
.list-leave-to {
    opacity: 0;
    transform: translateY(40px);
}


The transition phases are the same as the V-IF transitions. We can apply styles when an element is added or removed.

Once again, the name of the name prop matches the name- prefix of the CSS code.

The tag prop lets us specify the tag of the wrapper element. In our example, we make the wrapper element the p element.

State Transitions

We can also animate component state changes. To make this easy, we use the GreenSock library.

For instance, we can create an element that animates the number that we entered into the input as we change it, as follows:

index.js

new Vue({
  el: "#app",
  data: {
    number: 0,
    tweenedNumber: 0
  },
  computed: {
    animatedNumber() {
      return this.tweenedNumber.toFixed(0);
    }
  },
  watch: {
    number(newValue) {
      gsap.to(this.$data, { duration: 0.5, tweenedNumber: newValue });
    }
  }
});


index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <title>App</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.2.4/gsap.min.js"></script>
  </head>
  <body>
    <div id="app">
      <input v-model.number="number" type="number" step="20" />
      <p>{{ animatedNumber }}</p>
    </div>
    <script src="index.js"></script>
  </body>
</html>


In the code above, we have the number watcher that calls:

gsap.to(this.$data, { duration: 0.5, tweenedNumber: newValue });


to animate the number as it's being changed.

Conclusion

Creating transitions is easy with Vue. We can easily create them with the transition component for v-if transitions.

To animate v-for transitions, we can use the transition-group component.

We can create CSS transitions by using prefix-specific classes with the value that we pass in as the value of the name prop.

Also, we can create JavaScript animations with the listeners that we can add to our components, and we can use the Velocity library to add JavaScript animations.

Now that you have covered Vue animations, check out our other in-depth Vue tutorials, including our tutorial about protecting your source code.

Jscrambler

The leader in client-side Web security. With Jscrambler, JavaScript applications become self-defensive and capable of detecting and blocking client-side attacks like Magecart.

View All Articles