Search code examples
vue.jscss-transitionstransition

Vue List Move Transitions Only Work When Reordering, But Not Splicing


I have a Vue.js application (tested with both Vue 2 and 3) which displays a list of items in a transition-group which has a fade effect and should also be applying a list move transition. Anytime items are reordered (i.e an item that was prevously after a another item is moved before it), the move transition is applied, but if an item is removed with splice and the others stay in the same order, the move transition doesn't apply and the items after the removed one snap into place immediately. Here's a minimal example:

new Vue({
  el: '#app',
  data: () => ({
    list: [1, 2, 3]
  }),
  methods: {
    reorder() {
      this.list.unshift(this.list[this.list.length - 1])
        this.list.splice(this.list.length - 1, 1)
    },
    remove() {
        this.list.splice(1, 1)
    }
  }
})
button {
  display: block;
}

li {
  transition: 0.2s;
}

.v-enter-active, .v-leave-active {
  transition: opacity .5s;
}
.v-enter, .v-leave-to {
  opacity: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
  <button @click="reorder">Reorder</button>
  <button @click="remove" :disabled="list.length < 3">Remove Middle Item</button>

  <transition-group tag="ul">
    <li v-for="item in list" :key="item">{{ item }}</li>
  </transition-group>
</div>


Solution

  • This is my first answer in stack overflow, sorry in advanced if I'm not that accurate with my answer.

    From the vue.js documentation I managed to make it work by adding position absolute to the class .v-leave-active. I also modified the li transition duration to match with the opacity transition timing.

    I hope it helped!

    new Vue({
      el: '#app',
      data: () => ({
        list: [1, 2, 3]
      }),
      methods: {
        reorder() {
          this.list.unshift(this.list[this.list.length - 1])
            this.list.splice(this.list.length - 1, 1)
        },
        remove() {
            this.list.splice(1, 1)
        }
      }
    })
    button {
      display: block;
    }
    
    li {
      transition: 0.5s;
    }
    
    .v-enter-active, .v-leave-active {
      transition: opacity .5s;
    }
    
    .v-leave-active {
      position: absolute;
    }
    
    .v-enter, .v-leave-to {
      opacity: 0;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
    <div id="app">
      <button @click="reorder">Reorder</button>
      <button @click="remove" :disabled="list.length < 3">Remove Middle Item</button>
    
      <transition-group tag="ul">
        <li v-for="item in list" :key="item">{{ item }}</li>
      </transition-group>
    </div>