Vue 转换不会在按钮单击时触发 [英] Vue Transition not Triggering on button click

查看:23
本文介绍了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/vue@2.5.16/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,

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 转换不会在按钮单击时触发的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆