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
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>
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
new Vue({
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
}
<script src="https://cdn.jsdelivr.net/npm/vue@2.5.16/dist/vue.js"></script>
<div id="flip-list-demo" class="demo">
<button v-on:click="shuffle">Shuffle</button>
<transition-group name="a" tag="ul">
<li v-for="(item, index) in items" v-bind:key="item" v-show="index<5">
{{ item }}
</li>
</transition-group>
</div>