Intro to Vue.js: Animations

This is the fifth part in a five-part series about the JavaScript framework, Vue.js. In this last part of the series, we'll cover Animations (if you know me at all, you probably knew this was coming). This is not intended to be a complete guide, but rather an overview of the basics to get you up and running so you can get to know Vue.js and understand what the framework has to offer.

Article Series:

Some background

There are built-in <transition> and <transition-group> components that allow for both CSS and JS hooks. If you come from React, the concept behind the transition component will be familiar to you, because it works similarly to ReactCSSTransitionGroup in relationship to lifecycle hooks, but it has some notable differences that make nerds like me excited.

We'll start off by talking about CSS Transitions, then move on to CSS Animations, then we'll talk about JS Animation Hooks and then animating with Lifecycle Methods. Transitioning state is out of the scope of this article, but it is possible. Here's a well-commented Pen I made that does just that. I could probably be convinced to write that article too, once I take a long nap.

Transitions vs. Animations

Just in case you're confused by why Transitions and Animations have different sections in this article, let me explain that though they sound similar, they're a bit different. A transition basically works by interpolating the values from state to another. We can do great things with them, but they are rather simple. Here, to there, and back again.

Animations are a bit different in that you can make multiple states occur within one declaration. For instance, you could set a keyframe 50% into the animation, and then another totally different thing can occur at 70%, and so on. You can even chain many animations with delays for really complex movement. Animations have the ability to behave like transitions, where we only interpolate something from here to there, but transitions can't have multiple steps like an animation (not without some crazy hacky development that it's not really supposed to be used for.)

In terms of tools, both are useful. Think of transitions as a saw and animations as a powersaw. Sometimes you just need to saw one thing and it would be silly to go out and buy really expensive equipment. For other more robust projects, it makes more sense to make the powersaw investment.

Now that we have those basics down, let's talk about Vue!

CSS Transitions

Let's say we have a simple modal. The modal shows and hides on a click of a button. Based on the previous sections, we already know that we might: make a Vue instance with a button, make a child component from that instance, set the data on the state so that it toggles some sort of boolean and add an event handler to show and hide this child component. We could use v-if or v-show to toggle the visibility. We might even use a slot to pass the button toggle into the modal as well.

Now, we could just use <transition> out of the box. This will give us a v- prefix for some transition hooks we can use in our CSS. It will offer enter/leave which is the position that the animation starts with on the first frame, enter-active/leave-active while the animation is running- this is the one you’d place the animation properties themselves on, and enter-to/leave-to, which specifies where the element should be on the last frame.

I'm going to use a graphic from the docs to show this because I think it describes the classes as beautifully and clearly as possible:

Personally, I don't usually work with the default v- prefix. I'll always give the transition a name so that there are no collisions if I want to eventually apply another animation. It's not hard to do so, as you can see above, we simply added a name attribute to the transition component: name="fade".

The .fade-enter-active and .fade-leave-active classes will be where we apply the actual transition. This is normal CSS, you can pass in cubic-beziers for eases, delays, or specify other properties to transition. Truthfully, this would also work just as well if you placed the transition in these classes on the component classes themselves as a default. These don't necessarily need to be defined by the transition component hooks. They'll just chill there, and wait until that property changes and use it to transition if it does. (so you would still need the transition component and .fade-enter, .fade-leave-to). The one reason I do use it on the enter-active and leave-active classes is that I can reuse the same transition for other elements as well, and not run around the codebase applying the same default CSS to each instance.

Another thing to note here: I'm using ease-out for both active classes. This works and looks fine for something like opacity. But you may find that if you're transitioning properties such as transform, you might want to separate the two and use ease-out for the enter-active class and ease-in for the enter-leave class (or cubic-beziers that vaguely follow the same curve). I find it makes the animation look more... classy (har har).

You can see we've also set the .fade-enter and the .fade-to to opacity: 0. These will be the first and last positions of the animation, the initial state as it mounts, the end state as it unmounts. You may think you need to set opacity: 1 on .fade-enter-to, and .fade-leave, but that is unnecessary as it's the default state for the component, so it would be redundant. CSS transitions and animations will always use the default state unless told otherwise.

This works nicely! But what would happen if we wanted to make that background content fade out of view, so that the modal took center stage and the background lost focus? We can't use the <transition> component, as that component works based on something being mounted or unmounted, and the background is just sticking around. What we can do is transition classes based on the state, and use the classes to create CSS transitions that alter the background:

CSS Animation

Now that we understand how transitions work, we can build off of those core concepts to create some nice CSS animations. We'll still use the <transition> component, and we'll still give it a name, allowing us to have the same class hooks. The difference here will be that instead of just setting the final state and saying how we want it to interpolate between beginning and end, we'll use @keyframes in CSS to create fun and lovely effects.

In the last section, we talked a little about how you can designate a special name for the transition component that we can then use as class hooks. But in this section, we'll go a step further, and apply different class hooks to different sections of the animation. You'll recall that enter-active and leave-active is where all the juicy business of animating happens. We can set different properties on each of these class hooks, but we can go one step further and give special classes to each instance:

enter-active-class="toasty"
leave-active-class="bounceOut"

This means we can reuse those classes or even plug into the classes from CSS animation libraries.

For the bounce, we'd need a lot of keyframes if we want to do this in CSS (though in JS this could be one line of code), we also will use a SASS mixin to keep our styles DRY (don't repeat yourself). We've also designated the .ballmove-enter class to let the component know that it should start offscreen:

For rolling the ball out, you can see that we need to nest two different animations. This is because the transform is being applied to the entire child component, and spinning the whole thing would result in a huge rotation. So we'll move the component across the screen with a translation, and spin the ball within with a rotation:

Sweet, Sweet Transition Modes

Do you recall when I said that Vue offers some really nice sugary bits in transitions that make nerds like me happy? Here's a small one that I really love. If you try to transition one component in while another component is leaving, you'll end up with this really weird moment where both exist at the same time and then snap back into place (this small example from the Vue docs):

Vue offers transition modes, which will allow you to transition one component out while bringing another component in without any strange position flashing or blocking. It does so by ordering the transitioning instead of having them occur at the same time. There are two modes to choose from:

In-out: The current element waits until the new element is done transitioning in to fire

Out-in: The current element transitions out and then the new element transitions in.

Check out the demo below. You can see the mode- out-in on the transition component so that it appears that only one piece is flipping:

JS Animation

We have some nice JS hooks that are very easy to use or not use as we see fit for our animation. All hooks pass in the el parameter (short for element) except on the actual animation hooks, enter and leave, which also pass done as a parameter, which, you guessed it, is used to tell Vue that the animation is completed. You'll notice we’re also binding CSS to a falsy value to let the component know we'll be using JavaScript instead of CSS.

Two of the more interesting things to note in the above animation, I’m passing {onComplete:done} as a parameter to the Timeline instance, and I’m using the beforeEnter hook to place my TweenMax.set code, which allows me to set any properties on the words I need for the animation before it happens, in this case, things like transform-style: preserve-3d.

It's important to note that you can also set what you want for the animation directly in the CSS as the default state as well. People sometimes ask me how to decide what to set in the CSS and what to set in TweenMax.set. As a rule of thumb, I generally put any properties I need specifically for the animation into the TweenMax.set. That way if something in the animation changes and I need to update it, it's already part of my workflow.

Animations in Lifecycle Hooks

All of this is really nice, but what happens if you need to animate something very complex, something that works with a ton of DOM elements? This is a really nice time to use some lifecycle methods. In the following animation, we have used both the <transition> component as well as the mounted() method to create some animations.

But when a component first shows up and we have 30 elements or more animating, it would not longer be efficient to wrap each one in a separate transition component. So, we'll use the lifecycle methods we mentioned in section 3 of this series to hook into the same event that the transition hook is using under the hook: mounted()

We can really use either depending on what's more efficient and as you can see, you can create really complex effects. Vue offers a really beautiful and flexible API, not just for creating composable front-end architecture, but also for fluid movement and seamless transitions between views.

Conclusion

This series of articles is not intended to be documentation. Though we’ve covered a lot of ground, there's still so much more to explore: routing, mixins, server-side rendering, etc. There are so many amazing things to work with. Head over to the very excellent docs and this repo full of examples and resources to dig in further. There is also a book called The Majesty of Vue.js, and courses on Egghead.io and Udemy.

Many thanks to Robin Rendle, Chris Coyier, Blake Newman, and Evan You for proofreading various sections of this series. I hope this series conveys why I’m so excited about Vue and helps you get up and running trying out some of the material!

I think that can be done by using class binding.. im using “some what similar” effect your question is, but with a button click.. say my sidebar is 20% of the whole screen.. then im giving option to user to shrink to 5% using click.. all you need is just two classes.. and trigger one class when user clicked.. read about class binding here.. https://vuejs.org/v2/guide/class-and-style.html

This comment thread is closed. If you have important information to share, please contact us.