Vue Transition not按钮单击时触发 [英] Vue Transition not Triggering on button click

查看:70
本文介绍了Vue Transition not按钮单击时触发的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是Vue JS的新手,我正在创建一个缩略图查看器,我将获得一个图像和视频列表作为对象数组。首先,我将只显示5个项目,当用户单击上/下按钮时,我想垂直滑动缩略图。

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.

我创建了一个 codepen 通过引用StackOverflow上的一些链接。

I have created a codepen by referring some links on StackOverflow.

我正在使用Vue Transitions并且我的数据似乎是被动的但是当我点击时我无法看到平滑过渡(滑动到顶部/底部100px)在顶部和底部按钮上。

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代码:

<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代码:

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代码:

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]
            );
        }
    }
});


推荐答案

没有过渡问题你遇见是由:key =index引起的。

检查 Vue指南:v-for的关键


当Vue更新用v-for渲染的元素列表时,按
默认使用就地补丁策略。

When Vue is updating a list of elements rendered with v-for, by default it uses an "in-place patch" strategy.

此默认模式是有效的,但仅当您的列表
渲染输出不依赖于子组件状态或临时DOM
状态(例如表单输入值)时才适用。

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).

为Vue提供一个提示,以便它可以跟踪每个节点的标识,从而
重用并重新排序现有元素,您需要为每个项目提供唯一的键
属性。

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.

在您的代码中,您的五张图片始终具有相同的密钥= [1,2,3,4,5] ] ,所以Vue将 in-pla它们会导致转换没有被触发。

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.

所以只需修改:key =index:key =item.itemImageAlt,然后就可以了。

So simply modify the :key="index" to :key="item.itemImageAlt", then it works.

最后,调整css自己使转换效果符合您的要求。

Finally, adjust the css by yourself to make the transition effetcs meet your requirements.

以下是一个工作演示:

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

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆