If you are building user interfaces with Vuejs and suddenly animations knocked on UX doors, you don't have to worry. Vuejs offers a lot of flexibility to create any kind of animation (for elements and components) in response to changing state.
It provides two built-in components <Transition> and <TransitionGroup>, you can use <Transition> to wrap a single element/component or <TransitionGroup> to wrap a group of elements/components (a list was rendered by v-for for example), and then apply animations when:
- Toggling the visibility through
v-show. - Changing a rendering condition through
v-if. - inserting or removing from a list.
You can assign animation to <Transition> or <TransitionGroup>, and it will be applied on their children, the animation can be:
- css transition.
- css animation.
- a combination of css transition and css animation.
- javascript animation.
- a combination of javascript and css.
- custom css classes.
let's have examples for every animation ...
CSS Transition
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
.fade-enter-active, .fade-leave-active {
transition: opacity 0.3s ease-in-out;
}
The animation name is fade it was used as a prefix for all four classes. The css class .fade-enter-from is used to init the opacity by 0 when the element enters (fade-in), and .fade-leave-to class is used to set the desired opacity to 0 when the element leaves (fade-out).
The other two classes .fade-enter-active, .fade-leave-active are wrapping the enter/leave animation parts by declaring the css transition style.
Now let's assign the animation to the <Transition> component through the name prop:
<transition name="fade" mode="out-in">
<header v-if="animation1Toggle" class="font-mont border-b-2 border-black">
<h1 class="text-6xl capitalize font-bold">first</h1>
</header>
<header v-else class="font-mont border-b-2 border-black">
<h1 class="text-6xl capitalize font-bold">second</h1>
</header>
</transition>
<div class="mt-8">
<button class="text-4xl text-white font-ssp capitalize bg-black p-4" @click="animation1Toggle = !animation1Toggle">toggle fade</button>
</div>
When the animation1Toggle value changed (true/false), v-if will destroy or render the elements and as a result, the <Transition> component will load:
-
.fade-enter-from+.fade-enter-activeclasses when the element enters (means to be rendered) that will animate the opacity from 0 to 1. -
.fade-leave-to+.fade-leave-activeclasses when the element leaves (means to be destroyed) that will animate the opacity from 1 to 0.
Also prop mode=out-in is very important because it tells <Transition> component not to start the enter-animation until leave-animation is finished.
CSS Animation
.zoom-enter-active {animation: zoom-in 1s ease-in-out;}
.zoom-leave-active {animation: zoom-out 1s ease-in-out;}
@keyframes zoom-in {
from {transform: scale(0,0);}
to {transform: scale(1,1);}
}
@keyframes zoom-out {
from {transform: scale(1,1);}
to {transform: scale(0,0);}
}
Instead of css-transition you can use css-animation, within this example zoom-in and zoom-out animations were created by declaring .zoom-enter-active and .zoom-leave-active classes and keyframes, as a result a new animation called zoom will be available and can be applied to <Transition> component.
Combination of Transition and Animation
.zoom-fade-enter-active {
animation: zoom-in 0.3s ease-in-out;
transition: opacity 1s ease-in-out;
}
.zoom-fade-leave-active {
animation: zoom-out 1s ease-in-out;
transition: opacity 0.3s ease-in-out;
}
.zoom-fade-enter-from, .zoom-fade-leave-to {opacity: 0;}
@keyframes zoom-in {
from {transform: scale(0,0);}
to {transform: scale(1,1);}
}
@keyframes zoom-out {
from {transform: scale(1,1);}
to {transform: scale(0,0);}
}
Both CSS transition and CSS animation can be used together but, you have to tell <Transition /> component which duration to pick through prop type within this case <transition ... type="transition">.
Javascript Animation
const beforeEnter1 = (el) => {
console.log('before-enter');
}
const enter1 = (el, done) => {
const animation = el.animate([{'transform': 'scale(2, 0)'}, {}], {'duration': 300});
animation.onfinish = () => done();
}
const afterEnter1 = (el) => {
console.log('after-enter');
}
const beforeLeave1