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 when the user clicks Top / Bottom button, I want to slide the thumbnails vertically.
I have created a codepen by referring some links on StackOverflow.
I am using Vue Transitions and my data seems to be reactive but somehow I can't see the smooth transition (sliding to the top / bottom by 100px) when I click on the Top and Bottom buttons.
HTML Code:
<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>
<div :class="'slider' + (isSlidingToPrevious ? ' sliding-to-previous' : '')">
<transition-group name='list' tag="ul">
<li v-for="(item,index) in currentCarouselData" v-bind:key="index" class="list-item"><img :src="item.itemImage" :alt="item.itemImageAlt" /></li>
</transition-group>
</div>
<button @click="moveBottom" class="down" :disabled="currentBottomIndex === totalCount">Down</button>
</div>
</div>
<pre>
totalCount {{totalCount}}
currentTopIndex {{currentTopIndex}}
currentBottomIndex {{currentBottomIndex}}
itemsToDisplay {{itemsToDisplay}}
currentCarouselData {{currentCarouselData}}
</pre>
</div>
CSS / LESS Code:
.row-eq-height {
display: flex;
ul {
list-style-type: none;
display: flex;
flex-direction: column;
overflow: hidden;
height: auto;
border: 1px solid black;
}
li {
flex: 1;
width: 64px;
height: 64px;
position: relative;
margin: 8px 0;
border: 1px solid red;
img {
max-width: 100%;
max-height: 100%;
}
}
}
.list-leave-active,
.list-enter-active {
transition: 0.5s;
}
.list-enter {
transform: translate(0, 100px);
}
.list-leave-to {
transform: translate(0, -100px);
}
.sliding-to-previous {
.list-enter {
transform: translate(0, -100px);
}
.list-leave-to {
transform: translate(0, 100px);
}
}
Javascript / VUE Code:
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: {},
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]
);
}
}
});
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
No transition issue you met is caused by
:key="index"
.Check Vue Guide: key of v-for,
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: