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 theitems
array.startIndex
— a point in theitems
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 whereprevLength
comes in.transition
— determines which of the three transition effects to apply based onshouldFade
anddirection
.adjustedIndex
— we’ll see in a moment thatstartIndex
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 theitems
array bounds relative tostartIndex
.shifted
— rearrange theitems
array using theadjustedIndex
. 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 thedirection
property and adds direction ontostartIndex
.prev
andnext
— helper methods which callslide
with hard-coded values.add
andremove
— non-destructive push and pop.limit
— constrainsx
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).