Reputation: 3782
In my Vue app I have a simple toggle which renders a list when active. The list should not appear/disappear instantly. I want to have a smooth slide down transition on render and a smooth slide up transition on hide.
The following code shows what I have so far, does someone know how to make it work?
new Vue({
el: '#app',
data: () => ({
isOpen: true,
}),
});
.expand-enter-active,
.expand-leave-active {
overflow: hidden;
transition: height .5s linear;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button @click="isOpen = !isOpen">
is open: {{ isOpen }}
</button>
<transition name="expand">
<div v-if="isOpen">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</transition>
</div>
Upvotes: 3
Views: 6515
Reputation: 4330
You can use keyframe for the enter/mount
animation and then set height
to 0 while unmount
. If the height
is not known, you can use max-height
instead of height.
Vue.createApp({
data() {
return {
isOpen: false
};
}
}).mount('#app')
.expand {
height: 50px;
animation: slideDown 1s linear;
overflow: hidden;
}
.expand-leave-active.expand-leave-to {
transition: height 1s ease;
height: 0;
}
@keyframes slideDown{
from {
height: 0;
}
to {
height: 50px;
}
}
<script src="https://unpkg.com/vue@next"></script>
<div id="app">
<button @click="isOpen = !isOpen">
is open: {{ isOpen }}
</button>
<transition name="expand">
<div v-if="isOpen" class="expand">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</transition>
</div>
Upvotes: 1
Reputation: 1
use max-height
for animation because we couldn't animate height
prop according to this answer
new Vue({
el: '#app',
data: () => ({
isOpen: false,
}),
});
.expand-enter-active,
.expand-leave-active {
transition: max-height .5s ease;
max-height: 400px;
}
.expand-enter,
.expand-leave-to {
max-height: 0;
overflow: hidden;
}
.list {
box-shadow: 1px 0 10px #aaa;
padding: 8px;
width: 64px;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button @click="isOpen = !isOpen">
Open
</button>
<transition name="expand">
<div v-if="isOpen" class="list">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</div>
</transition>
</div>
Upvotes: 3
Reputation:
Please take a look this solution.
Vue.component('transition-collapse-height', {
template: `<transition
enter-active-class="enter-active"
leave-active-class="leave-active"
@before-enter="beforeEnter"
@enter="enter"
@after-enter="afterEnter"
@before-leave="beforeLeave"
@leave="leave"
@after-leave="afterLeave"
>
<slot />
</transition>`,
methods: {
/**
* @param {HTMLElement} element
*/
beforeEnter(element) {
requestAnimationFrame(() => {
if (!element.style.height) {
element.style.height = '0px';
}
element.style.display = null;
});
},
/**
* @param {HTMLElement} element
*/
enter(element) {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
element.style.height = `${element.scrollHeight}px`;
});
});
},
/**
* @param {HTMLElement} element
*/
afterEnter(element) {
element.style.height = null;
},
/**
* @param {HTMLElement} element
*/
beforeLeave(element) {
requestAnimationFrame(() => {
if (!element.style.height) {
element.style.height = `${element.offsetHeight}px`;
}
});
},
/**
* @param {HTMLElement} element
*/
leave(element) {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
element.style.height = '0px';
});
});
},
/**
* @param {HTMLElement} element
*/
afterLeave(element) {
element.style.height = null;
},
},
});
new Vue({
el: '#app',
data: () => ({
isOpen: true,
}),
});
.enter-active,
.leave-active {
overflow: hidden;
transition: height .5s linear;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<button @click="isOpen = !isOpen">
is open: {{ isOpen }}
</button>
<transition-collapse-height>
<div v-show="isOpen">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
</transition-collapse-height>
</div>
Upvotes: 5