Vue Transition Not Triggering on Button Click

Vue Transition not Triggering on button click

No transition issue you met is caused by :key="index".

Check Vue Guide: key of v-for,

When Vue is updating a list of elements rendered with v-for, by
default it uses an “in-place patch” strategy.

This default mode is efficient, but only suitable when your list
render output does not rely on child component state or temporary DOM
state (e.g. form input values).

To give Vue a hint so that it can track each node’s identity, and thus
reuse and reorder existing elements, you need to provide a unique key
attribute for each item.

In your codes, your five images always have the same key=[1, 2, 3, 4, 5], so Vue will in-place patch them, it causes the transition is not triggered.

So simply modify the :key="index" to :key="item.itemImageAlt", then it works.

Finally, adjust the css by yourself to make the transition effetcs meet your requirements.

Below is one working demo:

Vue.config.productionTip = false
new Vue({
el: "#app",
data() {
return {
totalCarouselData: [
{
itemImage:
"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",
itemImageAlt: "Test1"
},
{
itemImage:
"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",
itemImageAlt: "Test2"
},
{
itemImage:
"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",
itemImageAlt: "Test3"
},
{
itemImage:
"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",
itemImageAlt: "Test4"
},
{
itemImage:
"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",
itemImageAlt: "Test5"
},
{
itemImage:
"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",
itemImageAlt: "Test6"
},
{
itemImage:
"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",
itemImageAlt: "Test7"
}
],
currentCarouselData: [],
isSlidingToPrevious: false,
totalCount: 0,
currentTopIndex: 0,
currentBottomIndex: 0,
itemsToDisplay: 5
};
},
computed: {
computedCarouseData: function () { // added computed property
return this.totalCarouselData.slice(
-this.currentTopIndex,
this.itemsToDisplay-this.currentTopIndex
)
}
},
mounted() {
//At first show only 5 items
this.currentCarouselData = this.totalCarouselData.slice(
this.currentTopIndex,
this.itemsToDisplay
);
//Get Total Count
this.totalCount = this.totalCarouselData.length;
//Update current bottom index
this.currentBottomIndex = this.itemsToDisplay;
},
methods: {
moveTop() {
this.isSlidingToPrevious = true;
this.currentTopIndex += 1;
this.currentBottomIndex -= 1;
this.addToTopComputedArr(this.currentBottomIndex);
},
moveBottom() {
this.isSlidingToPrevious = false;
this.currentTopIndex -= 1;
this.currentBottomIndex += 1;
this.addToBottomComputedArr(this.currentBottomIndex);
},
addToBottomComputedArr(index) {
//Splice the first item
this.currentCarouselData.splice(0, 1);
//Add the next item to the array
this.currentCarouselData.push(this.totalCarouselData[index - 1]);
},
addToTopComputedArr(index) {
//Splice the last item
this.currentCarouselData.splice(index - 1, 1);
//Add item to the beginning of the array
this.currentCarouselData.unshift(
this.totalCarouselData[index - this.itemsToDisplay]
);
}
}
});
.row-eq-height {
display: flex;
}
.row-eq-height ul {
list-style-type: none;
display: flex;
flex-direction: column;
overflow: hidden;
height: auto;
border: 1px solid black;
}
.row-eq-height li {
flex: 1;
width: 64px;
height: 64px;
position: relative;
margin: 8px 0;
border: 1px solid red;
}
.row-eq-height li img {
max-width: 100%;
max-height: 100%;
}
.list-leave-active,
.list-enter-active {
transition: all 2s ease;
}
.list-enter {
opacity: 0;
transform: translateX(-300px);
}
.list-leave-to {
opacity: 0;
transform: translateX(-300px);
}
.sliding-to-previous .list-enter {
transform: translateY(-100px);
}
.sliding-to-previous .list-leave-to {
transform: translateY(100px);
}
.list-move {
transition: transform 1s;
}
<script src="https://unpkg.com/vue@2.5.16/dist/vue.js"></script>
<div id="app" class="container-fluid">
<div class="row row-eq-height">
<div class="thumbnail-container">
<button class="up" @click="moveTop" :disabled="currentTopIndex === 0">Top</button>
<button @click="moveBottom" class="down" :disabled="currentBottomIndex === totalCount">Down</button>
<div :class="'slider' + (isSlidingToPrevious ? ' sliding-to-previous' : '')">
<transition-group name='list' tag="ul">
<li v-for="(item,index) in computedCarouseData" v-bind:key="item.itemImageAlt" class="list-item"><img :src="item.itemImage" :alt="item.itemImageAlt" style=""/>{{item.itemImageAlt}}</li>
</transition-group>
</div>
</div>
</div>
<pre>
totalCount {{totalCount}}
currentTopIndex {{currentTopIndex}}
currentBottomIndex {{currentBottomIndex}}
itemsToDisplay {{itemsToDisplay}}
currentCarouselData {{computedCarouseData}}
</pre>
</div>

Why is my transition not working on leave?

It's probably caused by the use of different versions of Vue.

Your question is tagged vuejs3, so you are very likely using Vue 3, but, in the example you linked, vue version 2.6.4 is being used.

In vue 3, at least, you must use the directives v-if or v-show in the root element inside the Transition component for it to trigger the changes whenever the root component inside appears and disappears.

you can read more about transitions here

Vue 2 Transition not working

Have you added these CSS as well:

.fade-enter-active, .fade-leave-active {
transition: opacity .5s
}
.fade-enter, .fade-leave-to /* .fade-leave-active in <2.1.8 */ {
opacity: 0
}

I have tried to reproduce your code here with above CSS which works.

Vue transition-group is not triggering when using v-move class?

I suggest to use List Entering/Leaving Transitions instead of move transition and you should remove e.target.style.left = '100px'; :

<!DOCTYPE html>
<html>

<head>
<title>Page Title</title>

<meta name="viewport" content="width=device-width, initial-scale=1.0">

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Balsamiq+Sans:wght@400;700&family=Indie+Flower&family=Nunito+Sans:wght@400;600;700;800;900&display=swap" rel="stylesheet">

<style>
body {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin: 0;
font-family: 'Nunito', sans-serif;
}

.box {
position: relative;
border: 1px solid blue;
margin: 10px;
text-align: center;
width: 50px;
height: 50px;
user-select: none;
}

.test-group-enter-active,
.test-group-leave-active {
transition: all 1s;
}

.test-group-enter,
.test-group-leave-to {
opacity: 0;
transform: translateX(100px);
}
</style>
</head>

<body>

<div id='app'>
<test-comp></test-comp>
</div>
<script>
let arrary = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];

new Vue({
el: "#app",
components: {
"test-comp": {
template: `
<div style='display:inline-block;'>
<transition-group name='test-group'>
<div
class='box'
v-for='(all,ind) in arr'
:key='all'
@click='del(ind, $event)'>{{ all }}</div>
</transition-group>
</div>
`,
data() {
return {
arr: arrary
}
},
methods: {
del(ind, e) {

//e.target.style.left = '100px';
this.arr.splice(ind, 1);
}
}
}
}
});
</script>
</body>

</html>

Why is transition inside an element with v-if not entering the transition but leaving works?

You are missing the appear attribute.

By default, Vue will not animate when element is first inserted. As per docs:

If you also want to apply a transition on the initial render of a node, you can add the appear attribute:

<transition appear>
<!-- ... -->
</transition>

By default, this will use the transitions specified for entering and leaving. If you’d like however, you can also specify custom CSS classes:

<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class"
appear-active-class="custom-appear-active-class"
>
<!-- ... -->
</transition>

So just adding appear should fix the problem.

Can't change transition on the fly for a transition group

The problem lies in the timing of component update. You are switching the transition mode back to fade in the same update cycle as when the element is closed. Thus, when the next component update is triggered (by removal of the item), the transition is already switched back to fade. At this point, you may have guessed that all that needs to be done, is to switch the transition back in the next update, triggered by removal of the item:

   onSwipeUp (id) {
this.applySwipeTransition = true
this.$nextTick(() => {
this.closeItem(id)
this.$nextTick(()=>{
this.applySwipeTransition = false
})
})
}

Since there are no reasons to wait for component update to close the item, you can simplify the code a bit:

   onSwipeUp (id) {
this.applySwipeTransition = true
this.closeItem(id)
this.$nextTick(() => {
this.applySwipeTransition = false
})
}

Here is your working sandbox: https://codesandbox.io/s/vue-template-forked-60lkk?file=/src/App.vue

vue enter transition not working properly

There are a few problems in your CSS.

CSS Transitions and CSS Animations

A transition can be implemented using either CSS Transitions or CSS Animations. Your CSS incorrectly mixes the two concepts in this case.

In particular, the slideIn keyframes and .section-enter/.section-enter-to rules are effectively performing the same task of moving .section into view. However, this is missing a transition rule with a non-zero time, required to animate the change, so the change occurs immediately. The same issue exists for the slideOut keyframes and leave rules.

.section-enter {
top: 100vh;
}
.section-enter-to {
top: 0;
}
.section-enter-active {
transition: .5s; /* MISSING RULE */
}

.section-leave {
top: 0;
}
.section-leave-to {
top: -100vh;
}
.section-leave-active {
transition: .5s; /* MISSING RULE */
}

Removing the keyframes, and adding the missing rules (as shown above) would result in a working CSS Transition.

demo 1

Using CSS Animations

Alternatively, you could use keyframes with CSS Animations, where the animation is applied only by the *-active rules, and no *-enter/*-leave rules are used. Note your question contained unnecessary quotes in animation-name: 'slideIn';, which is invalid syntax and would be silently ignored (no animation occurs). I use a simpler shorthand in the following snippet (animation: slideIn 1s;).

.section-enter-active {
animation: slideIn 1s;
}
.section-leave-active {
animation: slideOut 1s;
}

@keyframes slideIn {
from {
top: 100vh;
}
to {
top: 0;
}
}
@keyframes slideOut {
from {
top: 0;
}
to {
top: -100vh;
}
}

demo 2

Optimizing CSS Transitions

You could also tweak your animation performance by using translateY instead of transitioning top.

/* top initially 0 in .wrapper */

.section-leave-active,
.section-enter-active {
transition: .5s;
}
.section-enter {
transform: translateY(100%);
}
.section-leave-to {
transform: translateY(-100%);
}

demo 3

Vue transition on static element not working

For the sake of completeness, I'm posting my comment as the answer.

It seems like you want the transition to happen when the element is first rendered. To achieve that, you will need to add the appear attribute:

<template> 
<transition name="slide" appear>
<div v-if="slideRight" class="menu-container">
<div class="menu-inner">
<Menu :items="items />
</div>
</div>
</transition>
</template>


Related Topics



Leave a reply



Submit