Vue Transition not Triggering on button click

前端 未结 2 993
北海茫月
北海茫月 2020-12-21 08:16

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

相关标签:
2条回答
  • 2020-12-21 08:56

    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>

    0 讨论(0)
  • 2020-12-21 08:57

    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

    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>

    0 讨论(0)
提交回复
热议问题