Vue 动画
<Transition> 组件
像我们在上面两个示例中所做的那样,使用纯 CSS 过渡和动画没有任何问题。
但幸运的是,Vue 为我们提供了内置的 <Transition>
组件,如果我们想要在使用 v-if
或 v-show
从应用程序中删除或添加元素时对其进行动画处理,因为用纯 CSS 动画很难做到这一点。
首先让我们创建一个应用程序,其中按钮添加或删除 <p>
标签:
示例
App.vue
:
<template>
<h1>Add/Remove <p> Tag</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<p v-if="exists">Hello World!</p>
</template>
<script>
export default {
data() {
return {
exists: false
}
},
computed: {
btnText() {
if(this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
}
</script>
<style>
p {
background-color: lightgreen;
display: inline-block;
padding: 10px;
}
</style>
运行示例 »
现在让我们将 <Transition>
组件包裹在 <p>
标签周围,看看如何以动画方式删除 <p>
标签。
当我们使用 <Transition>
组件时,我们会自动获得六个不同的 CSS 类,我们可以使用它们在添加或删除元素时设置动画。
在下面的示例中,我们将使用自动可用的类 v-leave-from
和 v-leave-to
在删除 <p>
标签时制作淡出动画:
示例
App.vue
:
<template>
<h1>Add/Remove <p> Tag</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<Transition>
<p v-if="exists">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
exists: false
}
},
computed: {
btnText() {
if(this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
}
</script>
<style>
.v-leave-from {
opacity: 1;
}
.v-leave-to {
opacity: 0;
}
p {
background-color: lightgreen;
display: inline-block;
padding: 10px;
transition: opacity 0.5s;
}
</style>
运行示例 »
六个 <Transition> 类
当我们使用 <Transition>
组件时,有六个类自动可供我们使用。
由于 <Transition>
组件内的元素是 added,我们可以使用前三个类来为该过渡设置动画:
- v-enter-from
- v-enter-active
- v-enter-to
由于元素在 <Transition>
组件内删除,我们可以使用接下来的三个类 :
- v-leave-from
- v-leave-active
- v-leave-to
注意:<Transition>
组件的根级别上只能有一个元素。
现在,让我们使用其中的四个类,以便在添加 <p>
标签和删除它时都可以制作动画。
示例
App.vue
:
<template>
<h1>Add/Remove <p> Tag</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<Transition>
<p v-if="exists">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
exists: false
}
},
computed: {
btnText() {
if(this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
}
</script>
<style>
.v-enter-from {
opacity: 0;
translate: -100px 0;
}
.v-enter-to {
opacity: 1;
translate: 0 0;
}
.v-leave-from {
opacity: 1;
translate: 0 0;
}
.v-leave-to {
opacity: 0;
translate: 100px 0;
}
p {
background-color: lightgreen;
display: inline-block;
padding: 10px;
transition: all 0.5s;
}
</style>
运行示例 »
我们还可以使用 v-enter-active
和 v-leave-active
在元素添加期间或删除期间设置样式或动画:
示例
App.vue
:
<template>
<h1>Add/Remove <p> Tag</h1>
<button @click="this.exists = !this.exists">{{btnText}}</button><br>
<Transition>
<p v-if="exists">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
exists: false
}
},
computed: {
btnText() {
if(this.exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
}
</script>
<style>
.v-enter-active {
background-color: lightgreen;
animation: added 1s;
}
.v-leave-active {
background-color: lightcoral;
animation: added 1s reverse;
}
@keyframes added {
from {
opacity: 0;
translate: -100px 0;
}
to {
opacity: 1;
translate: 0 0;
}
}
p {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
</style>
运行示例 »
Transition 'name' 属性
如果您有多个 <Transition>
组件,但您希望至少其中一个 <Transition>
组件具有不同的动画,则需要为 <Transition>
组件使用不同的名称来区分它们。
我们可以使用 name
属性选择 <Transition>
组件的名称,这也会更改过渡类的名称,以便我们可以为该组件设置不同的 CSS 动画规则。
<Transition name="swirl">
如果将过渡 name
属性值设置为 'swirl',自动可用的类现在将以 'swirl-' 开头,而不是 'v-':
- swirl-enter-from
- swirl-enter-active
- swirl-enter-to
- swirl-leave-from
- swirl-leave-active
- swirl-leave-to
在下面的示例中,我们使用 name
属性为 <Transition>
组件提供不同的动画。 一个 <Transition>
组件没有指定名称,因此使用自动生成的以"v-"开头的 CSS 类来指定动画。 另一个 <Transition>
组件被命名为"swirl",以便可以使用自动生成的以"swirl-"开头的 CSS 类为其指定动画规则。
示例
App.vue
:
<template>
<h1>Add/Remove <p> Tag</h1>
<p>本例中的第二个过渡的名称为"swirl",这样我们就可以使用不同的类名来将过渡分开。</p>
<hr>
<button @click="this.p1Exists = !this.p1Exists">{{btn1Text}}</button><br>
<Transition>
<p v-if="p1Exists" id="p1">Hello World!</p>
</Transition>
<hr>
<button @click="this.p2Exists = !this.p2Exists">{{btn2Text}}</button><br>
<Transition name="swirl">
<p v-if="p2Exists" id="p2">Hello World!</p>
</Transition>
</template>
<script>
export default {
data() {
return {
p1Exists: false,
p2Exists: false
}
},
computed: {
btn1Text() {
if(this.p1Exists) {
return 'Remove';
}
else {
return 'Add';
}
},
btn2Text() {
if(this.p2Exists) {
return 'Remove';
}
else {
return 'Add';
}
}
}
}
</script>
<style>
.v-enter-active {
background-color: lightgreen;
animation: added 1s;
}
.v-leave-active {
background-color: lightcoral;
animation: added 1s reverse;
}
@keyframes added {
from {
opacity: 0;
translate: -100px 0;
}
to {
opacity: 1;
translate: 0 0;
}
}
.swirl-enter-active {
animation: swirlAdded 1s;
}
.swirl-leave-active {
animation: swirlAdded 1s reverse;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
#p1, #p2 {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
#p2 {
background-color: lightcoral;
}
</style>
运行示例 »
JavaScript 过渡钩子
刚才提到的每个 Transition 类都对应一个事件,我们可以钩子该事件来运行一些 JavaScript 代码。
Transition 过渡类 | JavaScript 事件 |
---|---|
v-enter-from | before-enter |
v-enter-active | enter |
v-enter-to | after-enter enter-cancelled |
v-leave-from | before-leave |
v-leave-active | leave |
v-leave-to | after-leave leave-cancelled (v-show only) |
当下面的示例中发生 after-enter
事件时,会运行一个显示红色 <div>
元素的方法。
示例
App.vue
:
<template>
<h1>JavaScript 过渡钩子</h1>
<p>此代码钩子到 "after-enter",以便在初始动画完成后,运行一个显示红色 div 的方法。</p>
<button @click="pVisible=true">Create p-tag!</button><br>
<Transition @after-enter="onAfterEnter">
<p v-show="pVisible" id="p1">Hello World!</p>
</Transition>
<br>
<div v-show="divVisible">This appears after the "enter-active" phase of the transition.</div>
</template>
<script>
export default {
data() {
return {
pVisible: false,
divVisible: false
}
},
methods: {
onAfterEnter() {
this.divVisible = true;
}
}
}
</script>
<style scoped>
.v-enter-active {
animation: swirlAdded 1s;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
#p1, div {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
#p1 {
background-color: lightgreen;
}
div {
background-color: lightcoral;
}
</style>
运行示例 »
可以使用下例中的"Toggle"按钮来中断<p>
元素的enter过渡阶段,从而触发enter-cancelled
事件:
示例
App.vue
:
<template>
<h1>'enter-cancelled' 事件</h1>
<p>在输入动画完成之前再次单击切换按钮会触发'enter-cancelled'事件。</p>
<button @click="pVisible=!pVisible">Toggle</button><br>
<Transition @enter-cancelled="onEnterCancelled">
<p v-if="pVisible" id="p1">Hello World!</p>
</Transition>
<br>
<div v-if="divVisible">You interrupted the "enter-active" transition.</div>
</template>
<script>
export default {
data() {
return {
pVisible: false,
divVisible: false
}
},
methods: {
onEnterCancelled() {
this.divVisible = true;
}
}
}
</script>
<style scoped>
.v-enter-active {
animation: swirlAdded 2s;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 720deg;
scale: 1;
}
}
#p1, div {
display: inline-block;
padding: 10px;
border: dashed black 1px;
}
#p1 {
background-color: lightgreen;
}
div {
background-color: lightcoral;
}
</style>
运行示例 »
"appear" 属性
如果我们想要在页面加载时为某个元素添加动画效果,则需要在 <Transition>
组件上使用 appear
属性。
<Transition appear>
...
</Transition>
在此示例中,appear
属性在页面首次加载时启动动画:
示例
App.vue
:
<template>
<h1>'appear' 属性</h1>
<p>当页面打开时第一次呈现下面的 p 标签时,"appear"属性会启动动画。 如果没有 'appear' 属性,这个例子就没有动画。</p>
<Transition appear>
<p id="p1">Hello World!</p>
</Transition>
</template>
<style>
.v-enter-active {
animation: swirlAdded 1s;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
#p1 {
display: inline-block;
padding: 10px;
border: dashed black 1px;
background-color: lightgreen;
}
</style>
运行示例 »
元素之间的过渡
<Transition>
组件也可以用来在多个元素之间切换,只要我们使用 <v-if>
和 <v-else-if>
确保一次只显示一个元素:
示例
App.vue
:
<template>
<h1>元素之间的过渡</h1>
<p>单击按钮获取新图像。</p>
<p>新图像会在旧图像被删除之前添加。 我们将在下一个示例中使用 mode="out-in" 修复此问题。</p>
<button @click="newImg">Next image</button><br>
<Transition>
<img src="/img_pizza.svg" v-if="imgActive === 'pizza'">
<img src="/img_apple.svg" v-else-if="imgActive === 'apple'">
<img src="/img_cake.svg" v-else-if="imgActive === 'cake'">
<img src="/img_fish.svg" v-else-if="imgActive === 'fish'">
<img src="/img_rice.svg" v-else-if="imgActive === 'rice'">
</Transition>
</template>
<script>
export default {
data() {
return {
imgActive: 'pizza',
imgs: ['pizza', 'apple', 'cake', 'fish', 'rice'],
indexNbr: 0
}
},
methods: {
newImg() {
this.indexNbr++;
if(this.indexNbr >= this.imgs.length) {
this.indexNbr = 0;
}
this.imgActive = this.imgs[this.indexNbr];
}
}
}
</script>
<style>
.v-enter-active {
animation: swirlAdded 1s;
}
.v-leave-active {
animation: swirlAdded 1s reverse;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
img {
width: 100px;
margin: 20px;
}
img:hover {
cursor: pointer;
}
</style>
运行示例 »
mode="out-in"
在上面的示例中,在删除上一张图像之前添加下一张图像。
我们在 <Transition>
组件上使用 mode="out-in"
属性和属性值,以便在添加下一个元素之前完成元素的删除。
示例
除了 mode="out-in"
之外,此示例还使用计算值"imgActive",而不是我们在上一个示例中使用的"newImg"方法。
App.vue
:
<template>
<h1>mode="out-in"</h1>
<p>单击按钮获取新图像。</p>
<p>对于 mode="out-in",在当前图像被删除之前不会添加下一个图像。 与前面示例的另一个区别是,这里我们使用计算属性而不是方法。</p>
<button @click="indexNbr++">Next image</button><br>
<Transition mode="out-in">
<img src="/img_pizza.svg" v-if="imgActive === 'pizza'">
<img src="/img_apple.svg" v-else-if="imgActive === 'apple'">
<img src="/img_cake.svg" v-else-if="imgActive === 'cake'">
<img src="/img_fish.svg" v-else-if="imgActive === 'fish'">
<img src="/img_rice.svg" v-else-if="imgActive === 'rice'">
</Transition>
</template>
<script>
export default {
data() {
return {
imgs: ['pizza', 'apple', 'cake', 'fish', 'rice'],
indexNbr: 0
}
},
computed: {
imgActive() {
if(this.indexNbr >= this.imgs.length) {
this.indexNbr = 0;
}
return this.imgs[this.indexNbr];
}
}
}
</script>
<style>
.v-enter-active {
animation: swirlAdded 0.7s;
}
.v-leave-active {
animation: swirlAdded 0.7s reverse;
}
@keyframes swirlAdded {
from {
opacity: 0;
rotate: 0;
scale: 0.1;
}
to {
opacity: 1;
rotate: 360deg;
scale: 1;
}
}
img {
width: 100px;
margin: 20px;
}
img:hover {
cursor: pointer;
}
</style>
运行示例 »
使用动态组件进行过渡
我们还可以使用 <Transition>
组件来实现动态组件之间的切换动画:
示例
App.vue
:
<template>
<h1>动态组件的过渡</h1>
<p>Transition 组件包裹着动态组件,以便可以实现切换动画。</p>
<button @click="toggleValue = !toggleValue">Switch component</button>
<Transition mode="out-in">
<component :is="activeComp"></component>
</Transition>
</template>
<script>
export default {
data () {
return {
toggleValue: true
}
},
computed: {
activeComp() {
if(this.toggleValue) {
return 'comp-one'
}
else {
return 'comp-two'
}
}
}
}
</script>
<style>
.v-enter-active {
animation: slideIn 0.5s;
}
@keyframes slideIn {
from {
translate: -200px 0;
opacity: 0;
}
to {
translate: 0 0;
opacity: 1;
}
}
.v-leave-active {
animation: slideOut 0.5s;
}
@keyframes slideOut {
from {
translate: 0 0;
opacity: 1;
}
to {
translate: 200px 0;
opacity: 0;
}
}
#app {
width: 350px;
margin: 10px;
}
#app > div {
border: solid black 2px;
padding: 10px;
margin-top: 10px;
}
</style>
运行示例 »