Search code examples
javascriptvue.jsvuejs3vuejs-transitionvuejs-transition-group

Why do VueJS move animations only work in one direction?


I'm entirely stumped by this. For some reason, Vue3 will correctly apply the move animation class for a <transition-group/> containing a list of items, but only if the list is advanced forward. I've put together a CodePen for this and even with a very simple use case (listing numbers), the transition classes just don't get applied if you advance in any direction except backwards.

https://codepen.io/monitorjbl/pen/vYZPzXO

If you click the "-" button, the animation classes will be applied and you can see the move animations fire. However, clicking the "+" button results in no move animation classes being applied and no animation at all.


Solution

  • As I have mentioned in your comments, you are missing the enter and leave transition classes. Since you are only changing 3 elements of the array at any time, that means that 2 elements will remain in the list and will match your "move" class, but the 3 elements will be created/destroyed and you are not handling those cases.

    If you refer to the example given in the Vue documentation, you can see that enter/leave transition classes are needed to pull everything together. For example, you can add this to your CSS:

    .flip-list-enter-from,
    .flip-list-leave-to {
      opacity: 0;
    }
    
    .flip-list-leave-active {
      position: absolute;
    }
    

    Here is a proof-of-concept example:

    const Demo = {
      data() {
        return {
          min: 1,
          max: 5,
          list: []
        };
      },
      mounted() {
        this.populateList();
      },
      methods: {
        shiftDown() {
          this.min -= 3;
          this.max -= 3;
          this.populateList();
        },
        shiftUp() {
          this.min += 3;
          this.max += 3;
          this.populateList();
        },
        populateList() {
          this.list = [];
          for (let i = this.min; i <= this.max; i++) {
            this.list.push(i);
          }
        }
      }
    };
    
    Vue.createApp(Demo).mount("#flip-list-demo");
    body {
      margin: 30px;
    }
    
    .flip-list-item {
      transition: all 0.8s ease;
    }
    
    .flip-list-enter-from,
    .flip-list-leave-to {
      opacity: 0;
    }
    
    .flip-list-leave-active {
      position: absolute;
    }
    <script src="https://unpkg.com/vue@next"></script>
    <div id="flip-list-demo">
      <button @click="shiftDown" style="background-color:cyan">-</button>
      <button @click="shiftUp" style="background-color:yellow">+</button>
      <transition-group name="flip-list" tag="ul">
        <li v-for="item in list" :key="item" class="flip-list-item">
          {{ item }}
        </li>
      </transition-group>
    </div>