Skip to content Skip to sidebar Skip to footer

Vue Transition Not Triggering On Button Click

I am new to Vue JS and I am creating a thumbnail viewer wherein I'll be getting a list of images and videos as an array of objects. At first, I will be showing just 5 items and whe

Solution 1:

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 = falsenewVue({
  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 propertyreturnthis.totalCarouselData.slice(
        -this.currentTopIndex,
        this.itemsToDisplay-this.currentTopIndex
      )
    }
  },
  mounted() {
    //At first show only 5 itemsthis.currentCarouselData = this.totalCarouselData.slice(
      this.currentTopIndex,
      this.itemsToDisplay
    );
    //Get Total Countthis.totalCount = this.totalCarouselData.length;
    //Update current bottom indexthis.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 itemthis.currentCarouselData.splice(0, 1);
      //Add the next item to the arraythis.currentCarouselData.push(this.totalCarouselData[index - 1]);
    },
    addToTopComputedArr(index) {
      //Splice the last itemthis.currentCarouselData.splice(index - 1, 1);
      //Add item to the beginning of the arraythis.currentCarouselData.unshift(
        this.totalCarouselData[index - this.itemsToDisplay]
      );
    }
  }
});
.row-eq-height {
  display: flex;
}
.row-eq-heightul {
  list-style-type: none;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  height: auto;
  border: 1px solid black;
}
.row-eq-heightli {
  flex: 1;
  width: 64px;
  height: 64px;
  position: relative;
  margin: 8px0;
  border: 1px solid red;
}
.row-eq-heightliimg {
  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;
}
<scriptsrc="https://unpkg.com/vue@2.5.16/dist/vue.js"></script><divid="app"class="container-fluid"><divclass="row row-eq-height"><divclass="thumbnail-container"><buttonclass="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-groupname='list'tag="ul"><liv-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>

Solution 2:

Vue ( all Reactive frameworks ) use a Virtual DOM, as you change the one of your data elements or anything visually the page gets rerendered.

Coming to your question, it is not Smooth because once your value is changed it gets re-rendered.

Absolute / relative position your thumbnail, once you change currentCarouselData set top as 100px. Use setInterval to decrement top till you get 0 this will bring the sliding effect


EDIT transition using transition-group

newVue({
  el: '#flip-list-demo',
  data: {
    items: [1,2,3,4,5]
  },
  methods: {
    shuffle: function () {
      var x = this.items.splice(0,1)[0]
      console.log(x)
      this.items.push(x)
    }
  }
})
.a-move {
  transition: transform 1s;
}

.a-enter {
  transform: translateY(10px) 1s;
}

.a-leave {
  transform: translateY(10px) 1s;
  opacity : 0
}
<scriptsrc="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script><divid="flip-list-demo"class="demo"><buttonv-on:click="shuffle">Shuffle</button><transition-groupname="a"tag="ul"><liv-for="(item, index) in items"v-bind:key="item"v-show="index<5">
      {{ item }}
    </li></transition-group></div>

Post a Comment for "Vue Transition Not Triggering On Button Click"