本文介绍了Vue 转换不会在按钮单击时触发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我是 Vue JS 的新手,我正在创建一个缩略图查看器,我将在其中获取图像和视频列表作为对象数组.首先,我将只显示 5 个项目,当用户单击顶部/底部按钮时,我想垂直滑动缩略图.
我通过引用 StackOverflow 上的一些链接创建了一个 codepen.
我正在使用 Vue Transitions 并且我的数据似乎是反应性的,但不知何故,当我单击顶部"和底部"按钮时,我看不到平滑的过渡(滑动到顶部/底部 100 像素).
HTML 代码:
<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>
<button @click="moveBottom" class="down" :disabled="currentBottomIndex === totalCount">Down</button>
<预>totalCount {{totalCount}}currentTopIndex {{currentTopIndex}}currentBottomIndex {{currentBottomIndex}}itemsToDisplay {{itemsToDisplay}}currentCarouselData {{currentCarouselData}}
CSS/LESS 代码:
.row-eq-height {显示:弹性;ul{列表样式类型:无;显示:弹性;弹性方向:列;溢出:隐藏;高度:自动;边框:1px纯黑色;}李{弹性:1;宽度:64px;高度:64px;位置:相对;边距:8px 0;边框:1px纯红色;图片{最大宽度:100%;最大高度:100%;}}}.list-离开-活动,.list-enter-active {过渡:0.5s;}.list-输入 {变换:翻译(0, 100px);}.list-离开到{变换:翻译(0,-100px);}.滑动到上一个{.list-输入 {变换:翻译(0,-100px);}.list-离开到{变换:翻译(0, 100px);}}
Javascript/VUE 代码:
new Vue({el: "#app",数据() {返回 {总轮播数据:[{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"}],当前轮播数据:[],isSlidingToPrevious: 假,总数:0,当前最高指数:0,当前底部索引:0,itemsToDisplay: 5};},计算:{},安装(){//起初只显示5个项目this.currentCarouselData = this.totalCarouselData.slice(this.currentTopIndex,this.itemsToDisplay);//获取总计数this.totalCount = this.totalCarouselData.length;//更新当前底部索引this.currentBottomIndex = this.itemsToDisplay;},方法: {移动顶部(){this.isSlidingToPrevious = 真;this.currentTopIndex += 1;this.currentBottomIndex -= 1;this.addToTopComputedArr(this.currentBottomIndex);},移动底部(){this.isSlidingToPrevious = 假;this.currentTopIndex -= 1;this.currentBottomIndex += 1;this.addToBottomComputedArr(this.currentBottomIndex);},addToBottomComputedArr(index) {//拼接第一项this.currentCarouselData.splice(0, 1);//将下一项添加到数组中this.currentCarouselData.push(this.totalCarouselData[index - 1]);},addToTopComputedArr(index) {//拼接最后一项this.currentCarouselData.splice(index - 1, 1);//将item添加到数组的开头this.currentCarouselData.unshift(this.totalCarouselData[index - this.itemsToDisplay]);}}});
解决方案
您遇到的没有过渡问题是由 :key="index"
引起的.
检查 Vue 指南:v-for 的关键,
当 Vue 更新用 v-for 呈现的元素列表时,通过默认情况下,它使用就地补丁"策略.
这种默认模式是有效的,但仅适用于您的列表渲染输出不依赖于子组件状态或临时 DOM状态(例如表单输入值).
给 Vue 一个提示,以便它可以跟踪每个节点的身份,从而重用和重新排序现有元素,您需要提供唯一键每个项目的属性.
在您的代码中,您的五张图像始终具有相同的键=[1, 2, 3, 4, 5]
,因此 Vue 将就地修补它们, 导致转换未触发.
所以只需将 :key="index"
修改为 :key="item.itemImageAlt"
即可.
最后自己调整css,使过渡效果符合你的要求.
下面是一个工作演示:
Vue.config.productionTip = false新的 Vue({el: "#app",数据() {返回 {总轮播数据:[{项目图片:"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",itemImageAlt: "Test1"},{项目图片:"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",itemImageAlt: "Test2"},{项目图片:"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",itemImageAlt: "Test3"},{项目图片:"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",itemImageAlt: "Test4"},{项目图片:"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",itemImageAlt: "Test5"},{项目图片:"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",itemImageAlt: "Test6"},{项目图片:"https://www.publicdomainpictures.net/pictures/150000/velka/banner-header-tapete-145002399028x.jpg",itemImageAlt: "Test7"}],当前轮播数据:[],isSlidingToPrevious: 假,总数:0,当前最高指数:0,当前底部索引:0,itemsToDisplay: 5};},计算:{computerCarouseData: function () {//添加计算属性返回 this.totalCarouselData.slice(-this.currentTopIndex,this.itemsToDisplay-this.currentTopIndex)}},安装(){//起初只显示5个项目this.currentCarouselData = this.totalCarouselData.slice(this.currentTopIndex,this.itemsToDisplay);//获取总计数this.totalCount = this.totalCarouselData.length;//更新当前底部索引this.currentBottomIndex = this.itemsToDisplay;},方法: {移动顶部(){this.isSlidingToPrevious = 真;this.currentTopIndex += 1;this.currentBottomIndex -= 1;this.addToTopComputedArr(this.currentBottomIndex);},移动底部(){this.isSlidingToPrevious = 假;this.currentTopIndex -= 1;this.currentBottomIndex += 1;this.addToBottomComputedArr(this.currentBottomIndex);},addToBottomComputedArr(index) {//拼接第一项this.currentCarouselData.splice(0, 1);//将下一项添加到数组中this.currentCarouselData.push(this.totalCarouselData[index - 1]);},addToTopComputedArr(index) {//拼接最后一项this.currentCarouselData.splice(index - 1, 1);//将item添加到数组的开头this.currentCarouselData.unshift(this.totalCarouselData[index - this.itemsToDisplay]);}}});
.row-eq-height {显示:弹性;}.row-eq-height ul {列表样式类型:无;显示:弹性;弹性方向:列;溢出:隐藏;高度:自动;边框:1px纯黑色;}.row-eq-height li {弹性:1;宽度:64px;高度:64px;位置:相对;边距:8px 0;边框:1px纯红色;}.row-eq-height li img {最大宽度:100%;最大高度:100%;}.list-离开-活动,.list-enter-active {过渡:全2s缓和;}.list-输入 {不透明度:0;变换:translateX(-300px);}.list-离开到{不透明度:0;变换:translateX(-300px);}.sliding-to-previous .list-enter {变换:translateY(-100px);}.sliding-to-previous .list-leave-to {变换:translateY(100px);}.list-移动{转换:转换1s;}
<script src="https://unpkg.com/[email protected]/dist/vue.js"><;/脚本><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 calculateCarouseData" v-bind:key="item.itemImageAlt" class="list-item"><img :src="item.itemImage" :alt="item.itemImageAlt" style=""/>{{item.itemImageAlt}}</li></transition-group>
<预>totalCount {{totalCount}}currentTopIndex {{currentTopIndex}}currentBottomIndex {{currentBottomIndex}}itemsToDisplay {{itemsToDisplay}}currentCarouselData {{computedCarouselData}}
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]
);
}
}
});
解决方案
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:
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/[email protected]/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 转换不会在按钮单击时触发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
09-06 02:26