The vue-slide-up-down library working with pre-mounted elements only.
It just manipulating with the element's height and hidden
attribute.
Now what if I don't what the target element be mounted when it does not displaying?
If we try
<slide-up-down
v-if="active"
:active="active"
>
Only show this if "active” is true
</slide-up-down>
it will not be the animation because:
You need a separate variable controlling whether the component is rendered (I named it isRendered
below).
And a setter + getter computed (named rendered
below) which sets both active
and isRendered
to current value, but in different order:
true
: turn isRendered
on first, then set active
to true
in $nextTick
, so the animation is playedfalse
: turn active
to false
first, wait for animation to finish and then set isRendered
to false
.Vue2 demo:
Vue.component("slide-up-down", VueSlideUpDown)
new Vue({
el: "#app",
data: () => ({
isRendered: false,
active: false,
duration: 500
}),
computed: {
rendered: {
get() {
return this.isRendered
},
set(val) {
if (val) {
this.isRendered = val
this.$nextTick(() => {
this.active = val
})
} else {
this.active = val
setTimeout(() => {
this.isRendered = val
}, this.duration)
}
}
}
}
})
.wrap {
padding: 1rem;
border: 1px solid #ccc;
background-color: #f5f5f5;
}
button {
margin-bottom: 1rem;
}
<script src="https://unpkg.com/vue@2.7.14/dist/vue.min.js"></script>
<script src="https://unpkg.com/vue-slide-up-down@2.0.0/dist/vue-slide-up-down.umd.js"></script>
<div id="app">
<button @click="rendered = !rendered">Toggle</button>
<slide-up-down v-if="isRendered" v-bind="{ active, duration }">
<div class="wrap">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aut, consequatur
ut magnam, quos possimus, velit quam mollitia voluptate adipisci
reiciendis sapiente accusamus ullam ab voluptatem laborum non! Accusamus,
ullam, voluptatum.
</div>
</slide-up-down>
</div>
Vue3 demo:
const { createApp, reactive, computed, nextTick, toRefs } = Vue
const app = createApp({
setup() {
const state = reactive({
isRendered: false,
active: false,
duration: 500,
rendered: computed({
get() { return state.isRendered },
set(val) {
if (val) {
state.isRendered = val
nextTick(() => {
state.active = val
})
} else {
state.active = val
setTimeout(() => {
state.isRendered = val
}, state.duration)
}
}
})
})
return toRefs(state)
}
})
app.component("slide-up-down", Vue3SlideUpDown)
app.mount('#app')
.wrap {
padding: 1rem;
border: 1px solid #ccc;
background-color: #f5f5f5;
}
button {
margin-bottom: 1rem;
}
<script src="https://unpkg.com/vue@3.2.47/dist/vue.global.prod.js"></script>
<script src="https://unpkg.com/vue3-slide-up-down@1.2.5/dist/vue3-slide-up-down.umd.js"></script>
<div id="app">
<button @click="rendered = !rendered">Toggle</button>
<slide-up-down v-if="isRendered" v-model="active" v-bind="{ duration }">
<div class="wrap">
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Aut, consequatur
ut magnam, quos possimus, velit quam mollitia voluptate adipisci
reiciendis sapiente accusamus ullam ab voluptatem laborum non! Accusamus,
ullam, voluptatum.
</div>
</slide-up-down>
</div>
If you're gonna do this multiple times, you might want to extract it as a stand-alone component. Usage example:
<conditional-slide :rendered="condition" :duration="1000">
<div>content you want rendered based on `this.condition` (boolean)</div>
</conditional-slide>
Since the change now comes from the rendered
prop, you can move the computed setter code into a watch
:
In Vue 2:
<template>
<div>
<slide-up-down
v-if="isRendered"
v-bind="{ active, duration }"
>
<slot />
</slide-up-down>
</div>
</template>
<script>
import SlideUpDown from 'vue-slide-up-down'
export default {
components: { SlideUpDown },
props: {
rendered: {
type: Boolean,
default: false
},
duration: {
type: Number,
default: 500
}
},
data: () => ({
active: false,
isRendered: false
}),
watch: {
rendered: {
handler(val) {
if (val) {
this.isRendered = val
this.$nextTick(() => {
this.active = val
})
} else {
this.active = val
setTimeout(() => {
this.isRendered = val
}, this.duration)
}
},
immediate: true
}
}
}
</script>
Vue 3 example. Sandbox here.
<slide-up-down />
.