Transition 过度 - css 属性 transition、animation、transform - vue 内置组件
Transition 过度
Vue 提供了两个内置组件,可以帮助你制作基于状态变化的过渡和动画:
:在一个元素或组件,进入和离开 DOM 时,应用动画。
:在一个元素或组件,在
v-for
列表中,被插入、移动或被移除时,应用动画。
除了这两个组件,我们也可以通过其他技术手段来应用动画,比如切换 CSS 类或用状态绑定样式来驱动动画。这些其他的方法会在动画技巧章节中展开介绍到。
组件
是一个内置组件,这意味着它在任意别的组件中都可以被使用,无需注册。它可以在通过默认插槽传递给它的元素或组件上,应用进入和离开动画。进入或离开可以由以下其中一种触发:
- 由
v-if
所触发的切换 - 由
v-show
所触发的切换 - 由特殊元素
切换的动态组件
以下是最基本用法的示例:
Togglehello
/* 下面我们会解释这些类是做什么的 */ .v-enter-active, .v-leave-active { transition: opacity 0.5s ease; } .v-enter-from, .v-leave-to { opacity: 0; }
仅支持单个元素或组件作为其插槽内容。如果内容是一个组件,这个组件必须仅有一个根元素。当一个组件中的元素被插入或移除时,会发生下面这些事情:
- Vue 会自动检测目标元素是否应用了 CSS 过渡或动画。如果是,则一些 CSS 过渡类会在适当的时机被添加和移除。
- 如果有作为监听器的 JavaScript 钩子,这些钩子函数会在适当时机被调用。
- 如果没有探测到 CSS 过渡或动画、没有提供 JavaScript 钩子,那么 DOM 的插入、删除操作将在浏览器的下一个动画帧上执行。
基于 CSS 的过渡
过渡 CSS 类
一共有 6 个应用于进入与离开过渡效果的 CSS 类。
- v-enter-active:进入动画的生效状态,应用于整个进入动画阶段。这个 CSS 类,在元素被插入之前添加,在过渡/动画完成之后移除。这个类可以用来定义,进入动画的持续时间、延迟与速度曲线类型。
- v-enter-from:进入动画的起始状态。这个 CSS 类,在元素插入之前添加,在元素插入完成后的下一帧,移除。
- v-enter-to:进入动画的结束状态。这个 CSS 类,在元素插入完成后的下一帧,被添加(也就是v-enter-from被移除的同时),在过渡/动画完成之后移除。
- v-leave-active:离开动画的生效状态,应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡/动画完成之后移除。这个类可以用来定义,离开动画的持续时间、延迟与速度曲线类型。
- v-leave-from:离开动画的起始状态,在离开过渡效果被触发时,立即添加,在一帧后被移除。
- v-leave-to:离开动画的结束状态。这个 CSS 类,在一个离开动画被触发后的下一帧,被添加(也就是v-leave-from被移除的同时),在过渡/动画完成之后移除。
v-enter-active和v-leave-active给了我们为进入和离开动画指定不同速度曲线的能力。
为过渡命名
可以通过一个name
prop 来声明一种过渡:
......
对于一个有名字的过渡,它的过渡相关 CSS 类会以其名字而不是v
作为前缀。举个例子,上面被应用的 CSS 类将会是fade-enter-active
而不是v-enter-active
。这个fade
过渡的 CSS 类将会是这样:
.fade-enter-active, .fade-leave-active { transition: opacity 0.5s ease; } .fade-enter-from, .fade-leave-to { opacity: 0; }
CSS 的 transition
一般都会搭配原生 CSS 过渡一起使用,正如你在上面的例子中所看到的那样。这个
transition
CSS 属性是一个简写形式,使我们可以一次定义一个过渡的各个方面,包括需要执行动画的属性、持续时间和速度曲线。
下面是一个更高级的例子,使用不同的持续时间和速度曲线来过渡多个属性:
hello
/* 进入和离开动画可以使用不同 持续时间和速度曲线。 */ .slide-fade-enter-active { transition: all 0.3s ease-out; } .slide-fade-leave-active { transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1); } .slide-fade-enter-from, .slide-fade-leave-to { transform: translateX(20px); opacity: 0; }
CSS 的 animation
原生 CSS 动画和 CSS trasition 的应用方式基本上是相同的,只有一点不同,那就是*-enter-from不是在元素插入后立即移除,而是在一个animationend
事件触发时被移除。
对于大多数的 CSS 动画,我们可以简单地在*-enter-active和*-leave-active类下面声明它们。下面是一个示例:
Hello here is some bouncy text!
.bounce-enter-active { animation: bounce-in 0.5s; } .bounce-leave-active { animation: bounce-in 0.5s reverse; } @keyframes bounce-in { 0% { transform: scale(0); } 50% { transform: scale(1.25); } 100% { transform: scale(1); } }
自定义过渡类
你也可以向传递以下的props来指定自定义的过渡类:
- enter-from-class
- enter-active-class
- enter-to-class
- leave-from-class
- leave-active-class
- leave-to-class
你传入的这些类会覆盖相应阶段的默认类名。这个功能在你想要在 Vue 的动画机制下集成其他的第三方 CSS 动画库时非常有用,比如Animate.css:
hello
同时使用 transition 和 animation
Vue 需要附加事件侦听器,以便知道过渡何时结束。可以是transitionend
或animationend
,这取决于你所应用的 CSS 规则。如果你仅仅使用二者其中之一,Vue 可以自动探测到正确的类型。
然而在某些场景中,你或许想要在同一个元素上同时使用它们两个,举个例子,触发了一个 CSS 动画的同时,由于副作用触发了另一个 CSS 过渡。此时你需要显式地传入type prop 来声明,告诉 Vue 需要关心哪种类型,传入的值是animation或transition:
...
深层级过渡与显式过渡时间
尽管过渡类仅能应用在的直接子元素上,我们还是可以使用深层级的 CSS 选择器,使深层级的元素发生过渡。
Hello
/* 应用于深层级元素的规则 */ .nested-enter-active .inner, .nested-leave-active .inner { transition: all 0.3s ease-in-out; } .nested-enter-from .inner, .nested-leave-to .inner { transform: translateX(30px); opacity: 0; }
我们甚至可以在深层级的元素上添加一个过渡延迟,这会创建一个交错进入动画序列:
/* 延迟进入深层级元素以获得交错效果 */ .nested-enter-active .inner { transition-delay: 0.25s; }
然而,这会带来一个小问题。默认情况下,组件会通过监听过渡根元素上的第一个
transitionend
或者animationend
事件来尝试自动判断过渡何时结束。而在深层级的过渡中,期望的行为应该是等待所有内部元素的过渡完成。
在这种情况下,你可以通过向组件传入duration prop 来显式指定的过渡持续时间(以毫秒为单位)。总持续时间应该匹配延迟加上内部元素的过渡持续时间:
...
如果有必要的话,你也可以用对象的形式传入,分开指定进入和离开所需的时间:
...
性能考量
你可能注意到我们上面例子中展示的动画所用到的属性大多是transform
和opacity
之类的。用这些属性制作动画非常高效,因为:
- 他们在动画过程中不会影响到 DOM 结构,因此每一个动画帧都不会触发昂贵的 CSS 布局重新计算。
- 大多数的现代浏览器都可以在执行
transform
动画时利用 GPU 进行硬件加速。
相比之下,像height
或者margin
这样的属性会触发 CSS 布局变动,因此执行它们的动画效果更昂贵,需要谨慎使用。我们可以在CSS-Triggers这类的网站查询哪些属性会在执行动画时触发 CSS 布局变动。
...
在有了:css="false"
后,我们就全权自己负责控制什么时候过渡结束了。这种情况下对于@enter
和@leave
钩子来说,回调函数done()
就是必须的。否则,钩子将被同步调用,过渡将立即完成
可重用过渡
得益于 Vue 的组件系统,过渡是可以被重用的。要创建一个可被重用的过渡,我们需要为组件创建一个包装组件,并向内传入插槽内容:
/* 必要的 CSS... 注意:避免在这里使用 因为那不会应用到插槽内容上 */
现在 MyTransition 可以在导入后像内置组件那样使用了:
Hello
出现时过渡
如果你想在某个节点初次渲染时应用一个过渡效果,你可以添加appear attribute:
...
元素间过渡
除了通过v-if
/v-show
切换一个元素,我们也可以通过v-if
/v-else
/v-else-if
在几个组件间进行切换过:
Edit Save Cancel
过渡模式
在之前的例子中,进入和离开的元素都是在同时开始动画的,并且我们必须将它们设为position: absolute
以避免二者同时存在时出现的布局问题。
然而,在某些场景中这可能不是个好的方案,或者并不能符合行为预期。我们可能想要先执行离开动画,然后在其完成之后再执行元素的进入动画。手动编排这样的动画是非常复杂的,好在我们可以通过向传入一个mode prop 来实现这个行为:
...
也支持
mode="in-out"
,虽然这并不常用。
组件间过渡
也可以用在动态组件之间:
动态过渡
的props(比如
name
)也可以是动态的!这让我们可以根据状态变化动态地应用不同类型的过渡:
Fallthrough
当你使用 Vue 的过渡类约定规则定义了 CSS 过渡/动画,并想在它们之间切换时,这可能很有用。
你也可以根据你的组件的当前状态在 JavaScript 过渡钩子中应用不同的行为。在此篇的最后,我们可以得出结论,创建动态过渡的终极方式是创建可重用的过渡组件,这些组件接受prop来改变过渡的性质。现在在编写动画时,就真的只有你想不到,没有做不到的了。