Bound Transitions in Vue.js

Dynamically set transition effects

Sometimes we need use different transitions on the same element. For example, you might have a calendar component and you want to transition month changes from left to right and from right to left depending on user interaction. You might also want to use transitions based on the state of an object, for example a transition for an empty array and another for a non-empty array.

With Vue.js we can use the v-bind directive which can be applied to any attribute of an element or component. We can use this with the transition components name attribute:

<transition v-bind:name="transitionName">
<div v-if="someCondition">
{{text}}
</div>
</transition>

transitionName can be a prop, computed, a method, etc. As long as it returns a string which matches a transition defined in your css, it’ll work.

Here’s an example where an element either slides in from left to right, right to left or fades in/out depending on the state of some arbitrary array, or which button the user clicks to navigate the (also arbitrary) view.

We’ve defined a computed property, transition, which determines which of the three transitions to use and spits out either fade, out-left or out-right. These transition names match styles defined in our css:

.out-left-enter-active, .out-left-leave-active,
.out-right-enter-active, .out-right-leave-active,
.fade-enter-active, .fade-leave-active {
transition: all .3s ease;
}
.out-left-enter, .out-left-leave-to,
.out-right-enter, .out-right-leave-to,
.fade-enter, .fade-leave-to {
opacity: 0;
position: absolute;
}
.out-left-leave-to, .out-right-enter {
transform: translateX(-2.5em);
}
.out-left-enter, .out-right-leave-to {
transform: translateX(2.5em);
}

These transition effects are the same ones used in the Vue.js documentation.

As the user navigates the view, a new array is computed based on which direction the user is navigating. When the last element of the array is removed, the item should fade out. If there are no elements and a new one is added, it should fade in.

The example might be overly complex for what it actually achieves; I wanted to illustrate the idea with more than two transitions.

If you’re interested in how the code works, read on!


The Code

We’ve defined a few properties:

  • items — the arbitrary array of data which we’d like to render in our component.
  • prevLength — an integer which stores the previous length of the items array.
  • startIndex — a point in the items array which we’d like to render first.
  • direction — an integer which can be positive or negative. Could be substituted for a boolean, e.g. isRight: false.

Our template doesn’t care about any of these properties. We have defined some useful computed properties which form the bulk of the logic we need to render the correct item:

  • shouldFade — our requires that the last item fades out when removed, or fades in when the array is empty and a new item is added. This is where prevLength comes in.
  • transition — determines which of the three transition effects to apply based on shouldFade and direction.
  • adjustedIndex — we’ll see in a moment that startIndex can be mutated and that there is no limit on how much the value can change. This computed property adjusts that index to fall within the items array bounds relative to startIndex.
  • shifted — rearrange the items array using the adjustedIndex. We just chop of the first subset, and push it to the end of the array. Note, we’re using non-destructive array operators.
  • firstItem — a convenience property to keep our template neat. Returns first item from our arbitrary set of data.

The prevLength property needs to be updated somehow. We could update it from methods which change the items array. But that would mean we need to mutate this property everywhere the array is changed.

Vue.js exposes a watch API. Properties defined here correlate to properties defined in your data object or computed properties. Or anything that is reactive! Example: '$route'() { /* */ }

We’ll listen to changes to the length of our arrays length, and use the old value as our previous length. We don’t have to worry about that when we add logic to mutate the items array.

Our methods do some simple data manipulation:

  • slide(direction) — sets the direction property and adds direction onto startIndex.
  • prev and next — helper methods which call slide with hard-coded values.
  • add and remove — non-destructive push and pop.
  • limit — constrains x withy. e.g. limit(9, 6) === 3

The template defines a few buttons and registers click listeners which execute the prev, next, add and remove methods. We also render whatever is returned by the firstItem computed property, if anything is returned (it could be undefined if the array is empty).