Vue转换不会在单击按钮时触发

时间:2018-06-25 11:42:36

标签: javascript css3 vue.js vuejs2 vue-component

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

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

我正在使用Vue过渡,但是我的数据似乎是反应性的,但是当我单击“顶部”和“底部”按钮时,以某种方式我看不到平滑的过渡(向顶部/底部滑动100像素)。

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

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

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

2 个答案:

答案 0 :(得分:0)

Vue(所有Reactive框架)都使用虚拟DOM,因为您更改了一个数据元素或任何可视地重新呈现页面的内容。

提到您的问题,它不是平滑,因为一旦更改了值,它就会重新呈现。

您的缩略图的绝对/相对位置,一旦您将currentCarouselData更改为100px,就设置为缩略图。使用setInterval递减top直到您得到0,这将带来滑动效果


使用过渡组进行

EDIT过渡

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>

答案 1 :(得分:0)

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

选中Vue Guide: key of v-for

  

当Vue更新由v-for渲染的元素列表时,   默认情况下,它使用“就地补丁”策略。

     

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

     

给Vue一个提示,以便它可以跟踪每个节点的身份,从而   重用和重新排序现有元素,您需要提供唯一的密钥   每个项目的属性。

在您的代码中,您的五个图像始终具有相同的键= {[1, 2, 3, 4, 5],因此Vue将对其进行就地补丁,这将导致触发转换。

因此,只需将:key="index"修改为:key="item.itemImageAlt",即可正常工作。

最后,您可以自行调整css,以使过渡效果符合您的要求。

以下是一个有效的演示:

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>