Vue 动画使用 v-for
Vue 中内置的 <TransitionGroup> 组件帮助我们使用 v-for 为添加到页面中的元素设置动画。
<TransitionGroup> 组件
<TransitionGroup> 组件用于使用 v-for 创建的元素周围,以便在添加或删除这些元素时为其提供单独的动画。
在 <TransitionGroup> 组件内使用 v-for 创建的标记必须使用 key 属性进行定义。
只有当我们使用标记 prop 将 <TransitionGroup> 组件定义为特定标记时,它才会呈现为 HTML 标记,如下所示:
<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 类一些动画,我们可以给 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><divv-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>