David Atanda I'm a frontend developer who is passionate about technology and startups in Africa. I believe technology has the power to transform Africa into a first-world continent.

Understanding transitions and animations in Vue

7 min read 2109

Understanding Transitions and Animations in Vue

Adding transitions and animations to your web applications can help capture your users’ attention and jazz up the user experience. Even subtle effects and movements can lend your page a sense of action, especially if they play as the website is loaded.

To put what I’m saying into context, take a look at these login pages below (source: japa).

Example Login Page from japa

Example Login Page from japa

Which of these pages is more visually appealing? I’m sure most people would go with the second option. The effect is pretty minimal, but it provides more visual stimulation than the first, static page. On the other side of the coin, animations could be disturbing if excessive or too fast.

Transitions in Vue.js are particularly in cases where you need to attach or remove an element from the DOM, whether it’s due to routing, conditional statements, or whatever affects the element’s attachment to the DOM.

Consider the case below. We have our component and we’re using conditionals (v-if/v-else) to toggle the element from the webpage.

<template>
  <div>
   <div class="container">
  <button @click="display = !display">Switch</button>
     <div class="item" v-if="display">1</div>
   </div>
  </div>
</template>
<style scoped>
  body {
    align-content: center;
  }
    .container {
        display: grid;
        grid-gap: 20px;
        width: 500px;
        margin: 0 auto;
      }
    .item {
      background-color: blue;
      height: 100px;
    }
</style>

Then we can add the data attributes to bind to the HTML form.

[...]

<script>
export default {
  data () {
    return {
      display : false
    }
  }
}
</script>

To create a transition on this element as it’s been toggled, the first step is to wrap the element in a <transition> tag. Next, give it a unique name to be used when creating the transition classes.

This toggling doesn’t have to be due to v-if/v-else; it could be a dynamic component or anything else. Just wrap the element with a <transition> tag and it’ll work the same.

We made a custom demo for .
No really. Click here to check it out.

<button @click="display = !display">Switch</button>
<transition name="block">
 <div class="item" v-if="display">1</div>
</transition>

The transition tag only allows one element (or group of elements under one div wrapper) to display at any given time.

After inputting the name, this is where the transition classes come in. Transition classes are default classes that ship with Vue to determine when and how to attach or remove an element from the DOM.

  • .v-enter — This CSS class is only attached for one frame at the beginning of the transition while attaching to the DOM
  • .v-enter-active — This is where the process of attaching to the DOM occurs after the first frame
  • .v-leave — As with .v-enter, this is attached for the one frame at the removal of the DOM
  • .v-leave-active — This is the class that holds the style for the transitions that remove the element from the DOM.

In a real-life application, you’d replace the .v in front of the classes with the name of the transition you want it to affect. So your component would be structured like this:

  • .block-enter
  • .block-enter-active
  • .block-leave
  • .block-leave-active

Let’s have spin up a quick example of a fade transition using our initial component. The transition classes will look like this:

.block-enter {
        Opacity: 0;
}
.block-enter-active {
        transition : opacity 2s;
}
.block-leave {
}
.block-leave-active {
        transition: opacity 2s;
        Opacity: 0;
}

In .block-enter, the opacity is 0 by default. The element gradually displays itself with .block-enter-active with an opacity of 2s.

With .block-leave, the opacity is 1 by default because, at that moment, the element is still attached to the DOM. With .block-leave-active, it gradually disappears until the opacity ends up being 0.

Dynamic naming

The naming of the transition element doesn’t always have to be hardcoded, depending on your code or what you want the transition to look like. You can also bind the names of the transition, so it can be dynamic and not neccesarily hardcoded. It will be binded to a property and can be changed with a dropdown menu or button — again, depending on what action you wish to take. This is useful in cases where you have two separate transition styles and you need to switch between them.

In this instance below, we’ll use a dropdown menu.

<template>
  <div>
   <div class="container">
   <select v-model="animate">
     <option value="block">Fade</option>
     <option value="gradualFade">Slower fade</option>
   </select>
   <button @click="display = !display">Switch</button>
    <transition :name="animate">
      <div class="item" v-if="display">1</div>
    </transition>
   </div>
  </div>
</template>


[...]

<style scoped>
  body {
    align-content: center;
  }
    .container {
        display: grid;
        grid-gap: 20px;
        width: 500px;
        margin: 0 auto;
      }
    .item {
      background-color: blue;
      height: 100px;
    }

  .block-enter {
        Opacity: 0;
  }
  .block-enter-active {
        transition : opacity 2s;
  }
  .block-leave {
  }
  .block-leave-active {
        Transition: opacity 2s;
        Opacity: 0;
  }
  .gradualFade-enter {
        Opacity: 0;
  }
  .gradualFade-enter-active {
        transition : opacity 4s;
  }
  .gradualFade-leave {
  }
  .gradualFade-leave-active {
        Transition: opacity 4s;
        Opacity: 0;
  }
</style>
<script>
export default {
  data () {
    return {
      display : false,
      animate : 'block'
    }
  }
}
</script>

Animated properties

Animated properties can be used to handle animations, just like transition properties. They use keyframes the same way it works in traditional CSS.

Let’s recreate the fade effect, but this time with animated properties.

<template>
  <div>
    <div class="container">
    <button @click="display = !display">Switch</button>
    <transition name="block">
      <div class="item" v-if="display">1</div>
    </transition>
  </div>
  </div>
</template>


<style scoped>
  body {
    align-content: center;
  }
    .container {
        display: grid;
        grid-gap: 20px;
        width: 500px;
        margin: 0 auto;
      }
    .item {
      background-color: blue;
      height: 100px;
    }
    .block-enter {
    }
    .block-enter-active {
      animation : fade-in 1s 
    }
    .block-leave {
    }
    .block-leave-active {
       animation : fade-out 1s 
    }
    @keyframes fade-in {
      from {
        opacity : 0
      }
      to {
        opacity : 1;
      }
    }
    @keyframes fade-out {
      from {
        opacity : 1;
      }
      to {
        opacity : 0;
      }
    }
</style>
<script>
export default {
  data () {
    return {
      display : false
    }
  }
}
</script>

Using the same component we set up before, we can change the styles, this time using keyframes and adding the animation name and timing to the transition classes.

Mixing animations and transitions

You can use both animations and transitions on the same element, which can be especially useful when creating effects. To see this in action, let’s create slide and fade effects that execute simultaneously using both transitions and animations.

When being attached to the DOM, the box slides in from below while fading in simultaneously. When being removed from the DOM, it slides up and fades out.

<template>
  <div>
    <div class="container">
    <button @click="display = !display">Switch</button>
    <transition name="block">
      <div class="item" v-if="display">1</div>
    </transition>
  </div>
  </div>
</template>


<style scoped>
  body {
    align-content: center;
  }
    .container {
        display: grid;
        grid-gap: 20px;
        width: 500px;
        margin: 0 auto;
      }
    .item {
      background-color: blue;
      height: 100px;
      margin-bottom: 10px;
    }
    .block-enter {
      opacity: 0;
    }
    .block-enter-active {
      animation : slide-in 1s ease-out forwards;
      transition : opacity 1s;
    }
    .block-leave {
        opacity: 1;
    }
    .block-leave-active {
       animation : slide-out 1s ease-out forwards;
       transition : opacity 1s;
       opacity: 0;
    }
    @keyframes slide-in {
      from {
         transform: translateY(20px);
      }
      to {
         transform: translateY(0);
      }
    }
    @keyframes slide-out {
      from {
        transform: translateY(0);
      }
      to {
         transform: translateY(-20px);
      }
    }
</style>
<script>
export default {
  data () {
    return {
      display : false
    }
  }
}
</script>

We’re still using the same component. In the styles, we added separate animation keyframes for attaching and detaching the element.

For the slide-in keyframe, since it slides in from below, we added transform: translateY(20px) to transform: translateY(0).

For the slide-out keyframe, it’s sliding out upward, so we added transform : translateY(-20px) to transform: translateY(0).

Back to the transition classes; we gave the block-enter frame an opacity of 0.

.block-enter {
    Opacity: 0;
}

In .block-enter-active, it gradually changes the opacity to 1 (it’s 1 by default since it’s attached to the DOM), all within the 1s. The slide-in animation takes effect easing out with forwards to ensure it lands properly.

.block-enter-active {
      animation : slide-in 1s ease-out forwards;
     transition : opacity 1s;
}

To remove from the DOM, the animation is added to .block-leave-active. It slides out and the opacity changes to 0, still within 1s.

.block-leave-active {
   animation : slide-out 1s ease-out forwards;
   transition : opacity 1s;
   opacity: 0;
}

If the timing used within your animation and transition in any of the transition classes (attaching or dettaching) is different, there won’t be a balance and it’ll change unevenly. If that’s the case, add type to the transition class and tell it the exact timing to follow, whether transition or animation.

<transition name="block" type="animation">
      <div class="item" key="1" v-if="display">1</div>
    </transition>

If you want the animation to work on loading the page, you can change the display parameter to true and add appear to the transition tag.

<transition name="block" type="animation" appear>
      <div class="item" key="1" v-if="display">1</div>
    </transition>

Using the Animate.css library within the transition tag

Animate.css is a popular library for animations with which you might already be conversant. It’s useful even with Vue.

Libraries like Animate.css come with their own CSS classes aside from the transition classes we’ve been using. To handle that, we’ll take the following steps.

  1. Install the NPM package or add the CDN of animate.css to the index.html
    npm install animate.css --save
  2. Instead of adding a name attribute to the transition tag, add two new attributes: .enter-active-class and .leave-active-class. This is where you’ll add the classes from animate.css for attaching and removing from the DOM
     <transition 
      name="block"
      enter-active-class="animate__animated animate__bounce" 
      leave-active-class="animate__animated animate__pulse"         
    >
          <div class="item" key="1" v-if="display">1</div>
        </transition>
    

Transitioning between multiple elements

If you need to have two elements within a transition tag and display just one at a time due to a conditional v-if, take the following steps.

  1. Within the transition tag, give it a mode attribute. The mode can either be out-in or in-out. Out-in means the current element being displayed goes out before a new one comes in, and vice versa for in-out
  2. Then we give each of the two elements keys, this acts as a unique identifier for the transition tag wrapper to use for them. That’s it! They’ll switch places smoothly without any issue.
    <transition name="block" mode="out-in">
          <div class="item" key="1" v-if="display">1</div>
          <div class="item" key="2" v-if="!display">2</div>
        </transition>
    

Using transition-group to animate lists

Using transition-group is the same as using the transition-group tag, but for a list of elements displaying at once instead of just one. It’s useful in cases where you just want to loop through data you’re fetching from an API, maybe for a dashboard or something of that nature.

One fun fact is that transition-group is actually rendered to the DOM as a <span>, but on the other hand, transition isn’t rendered to the DOM.

Below is a transition-group tag wrapping some elements. We’re still using the same transition classes we used with the transition tag, but this time each separate element has a key. This key is used with the transition tag. It allows the transition-group to identify each element and can animate them as needed.

<template>
  <div>
    <div class="container">
    <button @click="display = !display">Switch</button>
    <transition-group name="block">
    <div class="item" key="1" v-if="display">1</div>
    <div class="item" key="2" v-if="display">2</div>
    <div class="item" key="3" v-if="display">3</div>
    </transition-group>
  </div>
  </div>
</template>


[...]

<style scoped>
  body {
    align-content: center;
  }
    .container {
        display: grid;
        grid-gap: 20px;
        width: 500px;
        margin: 0 auto;
      }
    .item {
      background-color: blue;
      height: 100px;
      margin-bottom: 10px;
    }
    .block-enter {
      opacity: 0;
    }
    .block-enter-active {
      animation : slide-in 1s ease-out forwards;
      transition : opacity 1s;
    }
    .block-leave {
        opacity: 1;
    }
    .block-leave-active {
       animation : slide-out 1s ease-out forwards;
       transition : opacity 1s;
       opacity: 0;
    }
    @keyframes slide-in {
      from {
         transform: translateY(20px);
      }
      to {
         transform: translateY(0);
      }
    }
    @keyframes slide-out {
      from {
        transform: translateY(0);
      }
      to {
         transform: translateY(-20px);
      }
    }
</style>
<script>
export default {
  data () {
    return {
      display : false
    }
  }
}
</script>

Conclusion

Before you go, it’s worth noting that there are animation libraries such as vue2-animate that can help you create seamless animations in Vue without having to write the code by yourself.

Feel free to reference the GitHub repo if you’re not clear on the code. There’s also a demo site site where you can check out all the effects.

Experience your Vue apps exactly how a user does

Debugging Vue.js applications can be difficult, especially when there are dozens, if not hundreds of mutations during a user session. If you’re interested in monitoring and tracking Vue mutations for all of your users in production, try LogRocket. https://logrocket.com/signup/

LogRocket is like a DVR for web apps, recording literally everything that happens in your Vue apps including network requests, JavaScript errors, performance problems, and much more. Instead of guessing why problems happen, you can aggregate and report on what state your application was in when an issue occurred.

The LogRocket Vuex plugin logs Vuex mutations to the LogRocket console, giving you context around what led to an error, and what state the application was in when an issue occurred.

Modernize how you debug your Vue apps - .

David Atanda I'm a frontend developer who is passionate about technology and startups in Africa. I believe technology has the power to transform Africa into a first-world continent.

Leave a Reply