Transition System

With Vue.js’ transition system you can apply automatic transition effects when elements are inserted into or removed from the DOM. Vue.js will automatically add/remove CSS classes at appropriate times to trigger CSS transitions or animations for you, and you can also provide JavaScript hook functions to perform custom DOM manipulations during the transition.

With the directive v-transition="my-transition" applied, Vue will:

  1. Try to find a JavaScript transition hooks object registered either through Vue.transition(id, hooks) or passed in with the transitions option, using the id "my-transition". If it finds it, it will call the appropriate hooks at different stages of the transition.

  2. Automatically sniff whether the target element has CSS transitions or CSS animations applied, and add/remove the CSS classes at the appropriate times.

  3. If no JavaScript hooks are provided and no CSS transitions/animations are detected, the DOM operation (insertion/removal) is executed immediately on next frame.

All Vue.js transitions are triggered only if the DOM manipulation was applied through Vue.js, either by a built-in directive, e.g. v-if, or by one of Vue’s instance methods, e.g. vm.$appendTo().

CSS Transitions

A typical CSS transition looks like this:

1
<div v-if="show" v-transition="expand">hello</div>

You also need to define CSS rules for .expand-transition, .expand-enter and .expand-leave classes:

1
2
3
4
5
6
7
8
9
10
11
12
.expand-transition {
transition: all .3s ease;
height: 30px;
padding: 10px;
background-color: #eee;
overflow: hidden;
}

.expand-enter, .expand-leave {
height: 0;
padding: 0 10px;
opacity: 0;
}

In addition, you can provide JavaScript hooks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
Vue.transition('expand', {

beforeEnter: function (el) {
el.textContent = 'beforeEnter'
},
enter: function (el) {
el.textContent = 'enter'
},
afterEnter: function (el) {
el.textContent = 'afterEnter'
},
enterCancelled: function (el) {
// handle cancellation
},

beforeLeave: function (el) {
el.textContent = 'beforeLeave'
},
leave: function (el) {
el.textContent = 'leave'
},
afterLeave: function (el) {
el.textContent = 'afterLeave'
},
leaveCancelled: function (el) {
// handle cancellation
}
})
hello

The classes being added and toggled are based on the value of your v-transition directive. In the case of v-transition="fade", the class .fade-transition will be always present, and the classes .fade-enter and .fade-leave will be toggled automatically at the right moments. When no value is provided they will default to .v-transition, .v-enter and .v-leave.

When the show property changes, Vue.js will insert or remove the <div> element accordingly, and apply transition classes as specified below:

In addition, if you remove an element when its enter transition is in progress, the enterCancelled hook will be called to give you the opportunity to clean up changes or timers created in enter. Vice-versa for leaving transitions.

All of the above hook functions are called with their this contexts set to the associated Vue instances. If the element is the root node of a Vue instance, that instance will be used as the context. Otherwise, the context will be the owner instance of the transition directive.

Finally, the enter and leave can optionally take a second callback argument. When you do so, you are indicating that you want to explicitly control when the transition should end, so instead of waiting for the CSS transitionend event, Vue.js will expect you to eventually call the callback to finish the transition. For example:

1
2
3
4
enter: function (el) {
// no second argument, transition end
// determined by CSS transitionend event
}

vs.

1
2
3
4
enter: function (el, done) {
// with the second argument, the transition
// will only end when `done` is called.
}

When multiple elements are being transitioned together, Vue.js batches them and only applies one forced layout.

CSS Animations

CSS animations are applied in the same way with CSS transitions, the difference being that v-enter is not removed immediately after the element is inserted, but on an animationend event.

Example: (omitting prefixed CSS rules here)

1
<span v-show="show" v-transition="bounce">Look at me!</span>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.bounce-enter {
animation: bounce-in .5s;
}

.bounce-leave {
animation: bounce-out .5s;
}

@keyframes bounce-in {
0% {
transform: scale(0);
}

50% {
transform: scale(1.5);
}

100% {
transform: scale(1);
}

}
@keyframes bounce-out {
0% {
transform: scale(1);
}

50% {
transform: scale(1.5);
}

100% {
transform: scale(0);
}

}
Look at me!

JavaScript Only Transitions

You can also use just the JavaScript hooks without defining any CSS rules. When using JavaScript only transitions, the done callbacks are required for the enter and leave hooks, otherwise they will be called synchronously and the transition will finish immediately. The following example registers a custom JavaScript transition using jQuery:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Vue.transition('fade', {
enter: function (el, done) {
// element is already inserted into the DOM
// call done when animation finishes.
$(el)
.css('opacity', 0)
.animate({ opacity: 1 }, 1000, done)
},
enterCancelled: function (el) {
$(el).stop()
},
leave: function (el, done) {
// same as enter
$(el).animate({ opacity: 0 }, 1000, done)
},
leaveCancelled: function (el) {
$(el).stop()
}
})

Then you can use it by providing the transition id to v-transition, same deal:

1
<p v-transition="fade"></p>

If the element with a JavaScript-only transition happens to have other CSS transitions or animations applied, it may intefere with Vue’s transition detection. In such cases you can add css: false to your transition object to explicitly disable Vue from sniffing CSS-related transitions.

Staggering Transitions

It’s possible to create staggering transitions when using v-transition with v-repeat. You can do this either by adding a stagger, enter-stagger or leave-stagger attribute to your transitioned element:

1
<div v-repeat="list" v-transition stagger="100"></div>

Or, you can provide a stagger, enterStagger or leaveStagger hook for finer-grained control:

1
2
3
4
5
6
7
Vue.transition('stagger', {
stagger: function (index) {
// increase delay by 50ms for each transitioned item,
// but limit max delay to 300ms
return Math.min(300, index * 50)
}
})

Example:

Next: Building Larger Apps.