Reputation: 609
I have a v-card
that I want to animate back and forth with one click.
If I click on an arrow to the left, the card should scroll to the right and then immediately scroll back in from the left. The other button should work the other way around.
The problem is, nothing happens here. What am I doing wrong?
My template:
<v-card>
<v-btn icon @click="back = false">
<v-icon>mdi-arrow-left</v-icon>
</v-btn>
<v-btn icon @click="back = true">
<v-icon>mdi-arrow-right</v-icon>
</v-btn>
</v-card>
<transition :name="back ? 'slide-fade' : 'slide-fade-reverse'">
<v-card max-width="200" class="mx-auto mt-5" height="80">
<span class="d-flex justify-center pt-7">{{back}}</span>
</v-card>
</transition>
My script:
data() {
return {
back: false,
}
},
My css:
/* Prev */
.slide-fade-enter-active {
transition: all .3s ease;
}
.slide-fade-leave-active {
transition: all .3s ease;
}
.slide-fade-enter {
transform: translateX(100px);
opacity: 0;
}
.slide-fade-leave-to {
transform: translateX(-100px);
opacity: 0;
}
/* Next */
.slide-fade-reverse-enter-active {
transition: all .3s ease;
}
.slide-fade-reverse-leave-active {
transition: all .3s ease;
}
.slide-fade-reverse-enter {
transform: translateX(-100px);
opacity: 0;
}
.slide-fade-reverse-leave-to {
transform: translateX(100px);
opacity: 0;
}
I made a Pen for this: https://codepen.io/Tenarius/pen/WNwdEve
Upvotes: 0
Views: 4190
Reputation: 371
I implemented this, and it is working:
// index.scss
$animation-duration: 0.15s;
.step-next-enter-active,
.step-next-leave-active,
.step-prev-enter-active,
.step-prev-leave-active {
transition:
transform #{$animation-duration} ease,
opacity #{$animation-duration} ease;
}
.step-next-enter-from,
.step-prev-leave-to {
transform: translateX(32px);
opacity: 0;
}
.step-next-leave-to,
.step-prev-enter-from {
transform: translateX(-32px);
opacity: 0;
}
.step-next-leave-from,
.step-next-enter-to,
.step-prev-leave-from,
.step-prev-enter-to {
transform: translateX(0);
}
<script setup lang="ts">
...
const animationDirection = ref<"step-next" | "step-prev">("step-next");
// Calculate the height of the current step for smooth transition
const currentStepHeight = ref<number>();
watch(
() => [store.currentStep],
([currentStep], [oldStep]) => {
if (currentStep !== oldStep) {
animationDirection.value =
currentStep > oldStep ? "step-next" : "step-prev";
}
},
{
flush: "pre",
}
);
// Get the current transitioning element height
function onEnter(e: Element): void {
const elHeight = e.getBoundingClientRect().height;
currentStepHeight.value = elHeight;
}
</script>
<template>
...
<div
class="overflow-hidden transition-all"
:style="{
height: currentStepHeight + 'px',
maxHeight: currentStepHeight + 'px',
}"
>
<Transition
:name="animationDirection"
mode="out-in"
@enter="onEnter"
>
<Component :is="steps[store.currentStep - 1]" />
</Transition>
</div>
...
</template>
Hope it helps!
Upvotes: 0
Reputation: 90103
In order for leave
and enter
transition to work, the <transition>
element has to have a v-if
condition. When it changes from false
to true
, the element gets inserted into DOM and animates according to enter
transition. When the condition changes from true
to false
, the leaving transition is performed and, when it ends, the element is removed from DOM.
However, you don't have such a condition. You're simply updating the cards contents and expect it to be removed from DOM and replaced by a new one.
In order to achieve the expected functionality you should use a list of cards (which would only contain the currently active card), coupled with using <transition-group>
which, internally, uses the same mechanics as transition
but the v-if
condition is whether the element is part of the collection or not.
In your case, the "collection" would be a filtered list of cards, containing only one card. With this technique, the leaving element gets the leave animation, while the entering element gets the enter animation, as the elements are actually removed and added to DOM, according to changes in your model.
See it working here.
Upvotes: 1
Reputation: 609
Since a transition needs leave and enter and thus the element has to "disappear" and "reappear", setTimout
can be used to build a workaround.
data() {
return {
back: false,
loading: false
}
},
methods: {
loadTimeout() {
this.loading = true
setTimeout(function(){
this.loading = false
}.bind(this), 500);
}
}
The card can then be expanded with v-show="!loading"
and the left- and right-buttons have to call the loadTimeout()
function.
Working example here
Upvotes: 0