Vue 2.0学习笔记:Vue的transition
动效在Web中一直是一个有争议的问题。动效做得好有助于在你的Web程序上锦上添花,甚至是留住你的用户,也可以具有较好的用户体验;反之,如果动效运用的不好,会给用户带来一种反感,让用户迅速地离开你的应用。怎么提供更友好的动效,并不是今天我们要讨论的重点,我们要讨论的是:**在Vue应用程序中如何添加动效?**在Vue中,提供了多种方法来给你的运用程序添加动效,比如CSS的transition
和animation
动效,以及在Vue的生命周期的钩子函数中操作DOM。甚至你还要以使用第三方动画库,比如GSAP或Velocity.js来制作动效。
在本文中,我们将先重点了解Vue中处理CSS的transition
的原理。有了这些知识,就可以开始创建自己的过渡动效。一旦掌握了这些基础知识,就可以快速掌握Vue中的transition
和animation
的全部功能。
transition
vs animation
在具体了解Vue中的transition
之前,咱们先简单的了来了解一下transition
和animation
之间的差异(这里所指的是CSS中两者的差异)。先上一张录制的动图:
两者的效果就如下图所示这样:
如果用代码来描述的话:
/* transition*/
.button {
background: blue;
transition: background;
&:hover {
background: red;
}
}
/* animation */
@keyframe spin {
0% {
color: red;
}
50% {
color: blue;
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.element {
animation: spin 1s ease-in-out 2s infinite;
}
也就是说,transition
是指一个元素从状态A
到状态B,即开始状态是A
,结束状态是B
。而其主要的目的是创建一个自然的演示,告诉用户的交互行为已经发生了更改(就是从A
变成了B
)。在现实的Web应用中这样的示例也非常的多。比如下拉菜单的效果就是其中的一个典型案例。默认情况下,下拉菜单是收缩闭合的(隐藏不见,就是我们所说的状态A
),当用户点击了或者鼠标悬浮在菜单项上,下拉菜单就会展开(下拉菜单可见,就是我们所说的状态B
)。这样的一个过程如果添加了transition
的话,就自钱的完成了闭合(隐藏)自动过渡到展开(可见)状态。比如下图所示:
animation
与transition
有很大的不同之处,从状态A
到状态B
之间有很多中间态,而这些被称为关键帧。因此,animation
也常常被称为帧动画,也有人称为补间动画。比如下图所示:
从上图我们也可以看出来,animation
有很多中间态,即,可以从A
到B
,再到C
,再到D
,甚至更多。其主要目的是不断展示某些东西正在改变。它们可以有结束状态,但与transition
不同,它们不限于两种状态。比如下面这样的一个示例,它不断地从一种状态变化到另一种状态,但它最终可能会结束。
在某种程度上,animation
只是transition
的一个超级集合,因为它添加了更多的中间状态。虽然transition
只是从状态A
到状态B,但
animation可以根据需要拥有任意多的中间状态。也就是说,只要理解了
transition的基本原理之后,在去理解
animation就不是什么一件难事了。这也就是我们今天为什么把重点放在
transition`上。
transition元素
在Vue中有一个<transition>
元素(即一个容器),它主要用来处理元素或组件上的transition
动效,CSS和JavaScript的animation
动效,而且会让你处理这些动效变得简地多。而在CSS的transition
动效中,<transition>
元素主要负责应用和取消类(元素的类名)。而你所要做的就是定义元素在transition
动效期间元素的样式。
<transition>
在Vue中使用非常的简单,把要带动效的元素放到这个容器之中,同时使用name
给其一个名称,比如fade
:
<transition name="fade">
<h1 v-if="show">Hello! W3cplus.com (^_^)</h1>
</transtion>
当在<transition>
容器中的元素在显示(插入)或隐藏(删除)时,Vue会自动嗅探到目标元素是否应用了CSS的transition
或animation
,如果是,在恰当的时机添加或删除CSS类名。
简单地说:
<transition>
是Vue已经封装好的一个组件,可以给任何元素和组件添加进入或离开过渡效果。
在Vue中的下面这几种情形会产生相应的过渡或动画效果:
- 条件渲染(使用
v-if
) - 条件显示(使用
v-show
) - 动态组件
- 组件根节点
比如下面这个示例:
<!-- FadeText -->
<template>
<div class="wrapper">
<transition name="fade">
<h1 v-if="isShow">{{ msg }}</h1>
</transition>
<button @click="isToggle" :class="isShow ? 'is-show' : 'is-hidden'">
{{ isShow ? "隐藏" : "显示" }}
</button>
</div>
</template>
<script>
export default {
name: "FadeText",
data() {
return {
isShow: true,
msg: "Hello! W3cplus.com (^_^)"
};
},
methods: {
isToggle() {
this.isShow = !this.isShow;
}
}
};
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>
效果如下:
上面这个简单的过渡效果使用的是CSS的transition
方式来实现。那么v-if
在切换元素h1
的时候发生了什么呢?
- 当
v-if
绑定的值isShow
在true
和false
之间切换的时候,<transition>
中的元素<h1>
会插入和删除之间进行切换 - 自动嗅探目标元素
h1
是否应用了CSS的transition
或animation
。如果是,则会在元素插入时添加CSS类名,并判断动画加载完之后删除CSS类名 - 如果过渡组件提供了JavaScript钩子函数(这部分后面会介绍),这些钩子函数在恰当的时机被调用。在这个示例中并没有用到JavaScript钩子函数,所以不会被执行
- 如果没有找到JavaScript钩子并且也没有检测到CSS的
transition
或animation
,DOM操作(插入或删除)在下帧中立即执行。(注意:此指浏览器逐帧动画机制,和 Vue 的nextTick
概念不同)
这个时候,通过浏览器的动画(Animations)选项可以看到动效的整个过程,如下图所示:
过渡的类名
在Vue中使用<transition>
制作过渡效果时,可以给过渡元素添加类名,在Vue中有六个类名,可以使用它们分别处理处理元素插入和删除时过渡效果。其中三个类用于处理元素插入时状态A
到状态B
过渡,另外三个类用于处理元素删除时状态A
到状态B
的过渡。
在插入或显示组件时发生enter
过渡,对应的类名是v-enter
、v-enter-active
和v-enter-to
;在隐藏或删除组件时发生leave
过渡,对应的类名是v-leave
、v-leave-active
和v-leave-to
。每个类对应的作用如下:
v-enter
:定义进入过渡的开始状态。在元素被插入之前生效,在元素被插入之后的下一帧移除。v-enter-active
:定义进入过渡生效时的状态。在整个进入过渡的阶段中应用,在元素被插入之前生效,在过渡/动画完成之后移除。这个类可以被用来定义进入过渡的过程时间,延迟和曲线函数。v-enter-to
: 定义进入过渡的结束状态。在元素被插入之后下一帧生效 (与此同时v-enter
被移除),在过渡/动画完成之后移除。v-leave
: 定义离开过渡的开始状态。在离开过渡被触发时立刻生效,下一帧被移除。v-leave-active
:定义离开过渡生效时的状态。在整个离开过渡的阶段中应用,在离开过渡被触发时立刻生效,在过渡/动画完成之后移除。这个类可以被用来定义离开过渡的过程时间,延迟和曲线函数。v-leave-to
: 定义离开过渡的结束状态。在离开过渡被触发之后下一帧生效 (与此同时v-leave
被删除),在过渡/动画完成之后移除。
用下图来阐述将会更清晰一些:
对于这些在过渡中切换的类名来说,如果你使用一个没有使用name
的 <transition>
,则 v-
是这些类名的默认前缀。如果你使用了 <transition name="fade">
,那么 v-enter
会替换为 fade-enter
,其他类名也是如此。另外,v-enter-active
和 v-leave-active
可以控制进入/离开过渡的不同的缓和曲线,有关于这部分将在后面会介绍。
结合起来,我们可以用一张图来表示Vue中过渡动效的生命周期,即动效开始、过程、结束对应的类的变化:
回到上面的示例中来,在元素h1
加载到DOM之前,h1
会添加fade-enter
类名,对应的动效也就被添加到该元素中,只对应一帧。因此,在DOM渲染时,将动画的opacity:0
应用到h1
元素上,此时该元素也就隐藏不可见。在动效的整个过程中,fade-enter-active
类也被添加到该元素中,在该示例中,动画被设置为.5s
。fade-leave
和fade-leave-active
也以相同的方式运用于h1
元素上。
在示例中,并没有显式的在fade-leave
类中设置opacity: 1
,那是因为opacity
的默认值已经是1
。这也就是为什么在fade-enter-active
结束时opacity
也不显式设置为1
的原因。
在Vue中,如果元素放置到<transition>
中,Vue会自动检测元素上的v-if
指令。比如上面的示例,如果isShow
的值为true
,那么h1
会渲染显示,当你点击按钮时,会自动切换isShow
的值。从而也控制了h1
元素的显示或隐藏(插入或删除)。
这里需要注意的一点是,Vue只能对<transition>
中的一个元素进行动效处理。相反,在任何给定的实例中,只能将<transition>
中的一个元素插入到DOM中。比如下同这个示例,在Vue中无法正常的工作:
<!-- ToggleAlert.vue -->
<template>
<div class="toogle-alert">
<button @click="isToggle" :class="isShow ? 'is-show' : 'is-hidden'">
{{ isShow ? "隐藏" : "显示" }}
</button>
<transition name="fade">
<div class="alert alert-info" v-if="isShow" key="info">{{ alertInfoMsg }}</div>
<div class="alert alert-error" v-if="isShow" key="error">{{ alertErrorMsg }}</div>
</transition>
</div>
</template>
<script>
export default {
name: "ToggleAlert",
data() {
return {
isShow: true,
alertInfoMsg: "Hello! W3cplus.com!",
alertErrorMsg: "Goodbye! W3cplus.com!"
};
},
methods: {
isToggle() {
this.isShow = !this.isShow;
}
}
};
</script>
<style scoped>
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-activ {
transition: opacity 0.5s cubic-bezier(1, 0.5, 0.8, 1);
}
</style>
这个时候浏览器会报错:
如果我们把另一个div
的v-if
用v-else
来替代:
在Vue中
<transition>
中有多个元素需要有动效效果时,需要使用<transition-group>
来替代,不然在Vue中则会报错。有关于<transition-group>
更详细的介绍,我们后续会单独花时间来阐述。另外,在<transition>
中有相同标签名的元素切换时,需要通过key
特性设置唯一的值来标记以让 Vue 区分它们,否则 Vue 为了效率只会替换相同标签内部的内容。即使在技术上没有必要,给在<transition>
组件中的多个元素设置key
是一个更好的实践。
现在,你看到的效果可以没有你想像的那么顺滑。这主要是因为,Vue在将第二个元素插入到DOM之前,并不会等第一个元素从DOM中完全删除。所以你看到效果就如上例所示,两个元素之间动效的切换似乎有点闪跳一样。不过我们可以通过transition
的过渡模式来处理。
过渡模式
上面的示例存在一个问题,先看下图这个效果:
div.alert-info
和div.alert-error
两个元素都被重绘了,一个离开过渡的时候另一个开始进入过渡。其实这也是<transition>
的默认行为,即进入和离开同时发生。
在Vue中提供了两种过渡模式来解决上述问题:
in-out
:新元素先进行过渡,完成之后当前元素过渡离开out-in
:当前元素先进行过渡,完成之后新元素过渡进入
这样我们就可以使用out-in
重写上面的示例:
<transition name="fade" mode="out-in">
<div class="alert alert-info" v-if="isShow" key="info">
{{ alertInfoMsg }}
</div>
<div class="alert alert-error" v-else="!isShow" key="error">
{{ alertErrorMsg }}
</div>
</transition>
效果如下:
设置初始渲染的过渡
在Vue中,可以通过appear
特性设置节点在初始渲染的过渡。
<transition name="fade" mode="out-in" appear>
<div class="alert alert-info" v-if="isShow" key="info">
{{ alertInfoMsg }}
</div>
<div class="alert alert-error" v-else="!isShow" key="error">
{{ alertErrorMsg }}
</div>
</transition>
这里默认和进入和离开过渡一样,同样也可以自定义 CSS 类名:
<transition
appear
appear-class="custom-appear-class"
appear-to-class="custom-appear-to-class"
appear-active-class="custom-appear-active-class"
>
<!-- ... -->
</transition>
同样可以对应的类名添加样式:
.custom-appear-class {
opacity: 0;
transform: translateX(100%);
}
.custom-appear-active-class {
transition: 2s;
}
比如上面的示例,按照上面的代码进行调整之后,可以发看到第一个.alert
插入到DOM中时,整个.alert
会从右侧移入进来,效果如下:
示例: 使用transition制作一个圆形菜单
通过上面的学习,我们接下来使用Vue的transition
来实现下面这样的一个圆形菜单效果:
这个案例其实非常的简单,通过一个<transition>
来触发多个子元素的过渡效果,我们只需要定义元素对应的过渡效果即可,而其他的事情都将交给Vue来搞定。
<transition name="move" mode="out-in" appear>
<ul class="menu" v-show="isShow">
<li
v-for="(item, index) in menus"
:key="index"
class="menuitem-wrapper"
>
<div class="icon-holder">
<a href="#" class="menu-item">
<i class="material-icons">{{ item }}</i>
</a>
</div>
</li>
</ul>
</transition>
只需要在对应的transition
类中控制transform
的样式:
.move-enter-active,
.move-leave-active {
transition: all 0.08s ease-in-out;
}
.move-enter,
.move-leave-to {
transform: scale(0);
}
最终效果如下:
总结
这篇文章主要介绍了Vue中如何使用<transition>
来实现transition
动效。这只是Vue中制作动效的最基础部分,但这些基础部分是帮助我们实现更为复杂的动效的基础。在接下来中我们将再一起探讨如何在Vue中实现CSS的animation
效果,以入如何和第三方库一起结合实现动效。如果感兴趣的话,欢迎持续关注后续的相关更新。sacai Nike LDWaffle + Blazer Mid Release Date