使用 v-for 的 Vue 动画

内置的<TransitionGroup>Vue 中的组件帮助我们对添加到页面的元素进行动画处理v-for

<TransitionGroup> 组件

这个<TransitionGroup>组件用于创建的元素周围v-for,在添加或删除这些元素时为其提供单独的动画。

创建的标签v-for在 - 的里面<TransitionGroup>组件必须定义为key属性。

这个<TransitionGroup>如果我们使用以下方法将组件定义为特定标签,则该组件仅呈现为 HTML 标签:tag道具,像这样:

<TransitionGroup tag="ol">
  <li v-for="x in products" :key="x">
    {{ x }}
  </li>
</TransitionGroup>

这是上面代码经过 Vue 渲染后的结果:

<ol>
  <li>Apple</li>
  <li>Pizza</li>
  <li>Rice</li>
</ol>

现在,我们可以添加 CSS 代码,以便在将新项目添加到列表时为其添加动画效果:

<style>
  .v-enter-from {
    opacity: 0;
    rotate: 180deg;
  }
  .v-enter-to {
    opacity: 1;
    rotate: 0deg;
  }
  .v-enter-active {
    transition: all 0.7s;
  }
</style>

在此示例中,只需将新项目添加到“products”数组即可对其进行动画处理:

示例

App.vue:

<template>
  <h3>The <TransitionGroup> Component</h3>
  <p>New products are given animations using the <TransitionGroup> component.</p>
  <input type="text" v-model="inpName"> 
  <button @click="addEl">Add</button>
  <TransitionGroup tag="ol">
    <li v-for="x in products" :key="x">
      {{ x }}
    </li>
  </TransitionGroup>
</template>

<script>
  export default {
    data() {
      return {
        products: ['Apple','Pizza','Rice'],
        inpName: ''
      }
    },
    methods: {
      addEl() {
        const el = this.inpName;
        this.products.push(el);
        this.inpName = null;
      }
    }
  }
</script>

<style>
  .v-enter-from {
    opacity: 0;
    rotate: 180deg;
  }
  .v-enter-to {
    opacity: 1;
    rotate: 0deg;
  }
  .v-enter-active {
    transition: all 0.7s;
  }
</style>
运行示例 »

添加和删​​除元素

当删除其他元素之间的元素时,其他元素将落入删除元素所在的位置。为了动画显示当删除元素时其余列表项如何就位,我们将使用自动生成的v-move类。

但首先,让我们看一个例子不是当一个元素被移除时,其他项目如何归位的动画:

示例

App.vue:

<template>
  <h3>The <TransitionGroup> Component</h3>
  <p>New products are given animations using the <TransitionGroup> component.</p>
  <button @click="addDie">Roll</button>
  <button @click="removeDie">Remove random</button><br>
  <TransitionGroup>
    <div v-for="x in dice" :key="x" class="diceDiv" :style="{ backgroundColor: 'hsl('+x*40+',85%,85%)' }">
      {{ x }}
    </div>
  </TransitionGroup>
</template>

<script>
  export default {
    data() {
      return {
        dice: [],
        inpName: ''
      }
    },
    methods: {
      addDie() {
        const newDie = Math.ceil(Math.random()*6);
        this.dice.push(newDie);
      },
      removeDie() {
        if(this.dice.length>0){
          this.dice.splice(Math.floor(Math.random()*this.dice.length), 1);
        }
      }
    },
    mounted() {
      this.addDie();
      this.addDie();
      this.addDie();
    }
  }
</script>

<style>
.v-enter-from {
  opacity: 0;
  translate: 200px 0;
  rotate: 360deg;
}
.v-enter-to {
  opacity: 1;
  translate: 0 0;
  rotate: 0deg;
}
.v-enter-active,
.v-leave-active {
  transition: all 0.7s;
}
.v-leave-from { opacity: 1; }
.v-leave-to { opacity: 0; }
.diceDiv {
  margin: 10px;
  width: 30px;
  height: 30px;
  line-height: 30px;
  vertical-align: middle;
  text-align: center;
  border: solid black 1px;
  border-radius: 5px;
  display: inline-block;
}
</style>
运行示例 »

正如您在上面的示例中所看到的,当删除某个项目时,删除的项目后面的项目会突然跳到新位置。要在删除某个项目时为其余项目设置动画,我们使用自动生成的v-move类。

这个v-move当被移除的项目离开时,类对其他元素进行动画处理,但存在一个问题:被移除的项目仍然存在并占据位置,直到被移除,因此v-move类不会有任何影响。给v-move类一些我们可以设置的动画position: absolute;v-leave-active类。什么时候position: absolute;在删除期间设置,删除的项目仍然可见,但不占用空间。

在此示例中,与上一个示例的唯一区别是第 14 行和第 17 行添加了两个新的 CSS 类:

示例

App.vue:

<style>
.v-enter-from {
  opacity: 0;
  translate: 200px 0;
  rotate: 360deg;
}
.v-enter-to {
  opacity: 1;
  translate: 0 0;
  rotate: 0deg;
}
.v-enter-active,
.v-leave-active,
.v-move {
  transition: all 0.7s;
}
.v-leave-active { position: absolute; }
.v-leave-from { opacity: 1; }
.v-leave-to { opacity: 0; }
.diceDiv {
  margin: 10px;
  width: 30px;
  height: 30px;
  line-height: 30px;
  vertical-align: middle;
  text-align: center;
  border: solid black 1px;
  border-radius: 5px;
  display: inline-block;
}
</style>
运行示例 »

一个更大的例子

让我们使用上面的示例作为新示例的基础。

在此示例中,当添加或删除新项目或对整个数组进行排序时,整个列表的动画效果变得更加清晰。

在这个例子中我们可以:

  • 通过点击删除项目
  • 对项目进行排序
  • 在列表中的随机位置添加新项目

示例

App.vue:

<template>
  <h3>The <TransitionGroup> Component</h3>
  <p>Items inside the <TransitionGroup> component are animated when they are created or removed.</p>
  <button @click="addDie">Roll</button>
  <button @click="addDie10">Roll 10 dice</button>
  <button @click="dice.sort(compareFunc)">Sort</button>
  <button @click="dice.sort(shuffleFunc)">Shuffle</button><br>
  <TransitionGroup>
    <div 
    v-for="x in dice" 
    :key="x.keyNmbr" 
    class="diceDiv" 
    :style="{ backgroundColor: 'hsl('+x.dieNmbr*60+',85%,85%)' }"
    @click="removeDie(x.keyNmbr)">
      {{ x.dieNmbr }}
    </div>
  </TransitionGroup>
</template>

<script>
  export default {
    data() {
      return {
        dice: [],
        keyNumber: 0
      }
    },
    methods: {
      addDie() {
        const newDie = {
          dieNmbr: Math.ceil(Math.random()*6),
          keyNmbr: this.keyNumber
        };
        this.dice.splice(Math.floor(Math.random()*this.dice.length),0,newDie);
        this.keyNumber++;
      },
      addDie10() {
        for(let i=0; i<10; i++) {
          this.addDie();
        }
      },
      compareFunc(a,b){
        return a.dieNmbr - b.dieNmbr;
      },
      shuffleFunc(a,b){
        return Math.random()-0.5;
      },
      removeDie(key) {
        const pos = this.dice.map(e => e.keyNmbr).indexOf(key);
        this.dice.splice(pos, 1);
      }
    },
    mounted() {
      this.addDie10();
    }
  }
</script>

<style>
.v-enter-from {
  opacity: 0;
  scale: 0;
  rotate: 360deg;
}
.v-enter-to {
  opacity: 1;
  scale: 1;
  rotate: 0deg;
}
.v-enter-active,
.v-leave-active,
.v-move {
  transition: all 0.7s;
}
.v-leave-active { position: absolute; }
.v-leave-from { opacity: 1; }
.v-leave-to { opacity: 0; }
.diceDiv {
  margin: 10px;
  width: 30px;
  height: 30px;
  line-height: 30px;
  vertical-align: middle;
  text-align: center;
  border: solid black 1px;
  border-radius: 5px;
  display: inline-block;
}
.diceDiv:hover {
  cursor: pointer;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
#app {
  position: relative;
}
</style>
运行示例 »

Vue练习

通过练习测试一下

练习:

用于对使用 v-for 创建或删除的元素进行动画处理的 Vue 特定组件的名称是什么?

< tag="ol">
  <li v-for="x in products" :key="x">
    {{ x }}
  </li>
</>

开始练习