Vue 学习笔记 - 内置组件 - Transition

Transition

<Transition> 会在一个元素或组件进入和离开 DOM 时应用动画。本章节会介绍如何使用它。

内置组件可以在任意的组件中被使用,无需注册。它可以将进入和和离开动画应用到传递给它的元素或组件上

进入或离开的触发条件

  • 由 v-if 所触发的切换
  • 由 v-show 所触发的切换
  • 由特殊元素 切换的动态组件
  • 改变特殊的 key 属性
1
2
3
4
<button @click="show = !show">Toggle</button>
<Transition>
<p v-if="show">hello</p>
</Transition>
1
2
3
4
5
6
7
8
9
.v-enter-active,
.v-leave-active {
transition: opacity 0.5s ease; /* 0.5s内透明:慢速开始,然后变快,然后慢速结束的过渡效果(cubic-bezier(0.25,0.1,0.25,1))(相对于匀速,中间快,两头慢)。*/
}

.v-enter-from,
.v-leave-to {
opacity: 0;
}

<Transition> 仅支持单个元素或组件作为其内容。如果内容是一个组件,这个组件必须仅有一个根元素。

当一个 <Transition> 组件中的元素被插入或移除时,会发生下面这些事情:

  1. Vue会自动检测目标元素是否应用了CSS过渡或动画,如果是,则一些CSS过渡class会在适当的时机被添加和移除
  2. 如果有作为监听器的Javascript钩子,这些钩子函数会在适当时机被调用
  3. 如果没有探测到CSS过渡或动画,也没有提供JavaScript钩子,那么DOM的插入,删除操作将在浏览器的下一个动画帧后执行

基于CSS的过渡效果

一共有6个应用于进入与离开过渡效果的CSS class

17445933468144.jpg

  1. v-enter-from: 进入动画的起始状态。在元素插入之前添加,在元素插入完成后的下一帧移除。
  2. v-enter-active: 进入动画的生效状态。应用于整个动画阶段。在元素被插入之前添加,在过渡或动画完成之前移除。这个class可以被用来定义动画的持续时间,延迟与速度曲线类型
  3. v-enter-to: 进入动画的结束状态。在元素插入完成后的下一帧被添加(也就是v-enter-from被移除的同时),在过渡或动画完成之后移除
  4. v-leave-from: 离开动画的起始状态。在离开过渡效果被触发时立即添加,在一帧后被移除。
  5. v-leave-active:离开动画的生效状态。应用于整个离开动画阶段。在离开过渡效果被触发时立即添加,在过渡或动画完成之后移除。这个 class 可以被用来定义离开动画的持续时间、延迟与速度曲线类型。
  6. v-leave-to:离开动画的结束状态。在一个离开动画被触发后的下一帧被添加 (也就是 v-leave-from 被移除的同时),在过渡或动画完成之后移除。

为过渡效果命名

1
2
3
<Transition name="fade">
...
</Transition>

那前面那几个对应的class会以其姓名而不是v作为前缀

1
2
3
4
5
6
7
8
9
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
opacity: 0;
}

CSS的transition

<Transition> 一般都会搭配原生 CSS 过渡一起使用,这个 transition CSS 属性是一个简写形式,使我们可以一次定义一个过渡的各个方面,包括需要执行动画的属性、持续时间和速度曲线。

1
2
3
<Transition name="slide-fade">
<p v-if="show">hello</p>
</Transition>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
进入和离开动画可以使用不同
持续时间和速度曲线。
*/
.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 transition 的应用方式基本上是相同的,只有一点不同,那就是 *-enter-from 不是在元素插入后立即移除,而是在一个 animationend 事件触发时被移除。

对于大多数的 CSS 动画,我们可以简单地在 *-enter-active*-leave-active class 下声明它们

1
2
3
4
5
<Transition name="bounce">
<p v-if="show" style="text-align: center;">
Hello here is some bouncy text!
</p>
</Transition>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.bounce-enter-active {
animation: bounce-in 0.5s; /* 展示时0.5s完成bounce-in这个动画 */
}
.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);
}
}

关键帧 @keyframes 规则通过在动画序列中定义关键帧(或 waypoints)的样式来控制 CSS 动画序列中的中间步骤。

即先0 -> 放大1.25 -> 原来大小

自定义过渡 class

也可以向 <Transition> 传递以下的 props 来指定自定义的过渡 class

传入的class会覆盖相应阶段的默认class名。这个功能在想要集成其它的第三方CSS动画库时非常有用

1
2
3
4
5
6
7
8
<!-- 假设你已经在页面中引入了 Animate.css -->
<Transition
name="custom-classes"
enter-active-class="animate__animated animate__tada"
leave-active-class="animate__animated animate__bounceOutRight"
>
<p v-if="show">hello</p>
</Transition>

同时使用transition和animation

Vue 需要附加事件监听器,以便知道过渡何时结束。可以是 transitionendanimationend,这取决于你所应用的 CSS 规则。如果你仅仅使用二者的其中之一,Vue 可以自动探测到正确的类型。

然而在某些场景中,你或许想要在同一个元素上同时使用它们两个。举例来说,Vue 触发了一个 CSS 动画,同时鼠标悬停触发另一个 CSS 过渡。此时你需要显式地传入 type prop 来声明,告诉 Vue 需要关心哪种类型,传入的值是 animationtransition

1
<Transition type="animation">...</Transition>

深层级过渡与显式过渡时长

尽管过渡 class 仅能应用在 <Transition> 的直接子元素上,我们还是可以使用深层级的 CSS 选择器,在深层级的元素上触发过渡效果:

1
2
3
4
5
6
7
<Transition name="nested">
<div v-if="show" class="outer">
<div class="inner">
Hello
</div>
</div>
</Transition>
1
2
3
4
5
6
7
8
9
10
11
12
13
/* 应用于嵌套元素的规则 */
.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;
}

/* ... 省略了其他必要的 CSS */

然而,这会带来一个小问题。默认情况下,<Transition> 组件会通过监听过渡根元素上的第一个 transitionend 或者 animationend 事件来尝试自动判断过渡何时结束。而在嵌套的过渡中,期望的行为应该是等待所有内部元素的过渡完成。

在这种情况下,你可以通过向 <Transition> 组件传入 duration prop 来显式指定过渡的持续时间 (以毫秒为单位)。总持续时间应该匹配延迟加上内部元素的过渡持续时间:

1
<Transition :duration="{ enter: 500, leave: 800 }">...</Transition>

JavaScript 钩子

1
2
3
4
5
6
7
8
9
10
11
12
<Transition
@before-enter="onBeforeEnter"
@enter="onEnter"
@after-enter="onAfterEnter"
@enter-cancelled="onEnterCancelled"
@before-leave="onBeforeLeave"
@leave="onLeave"
@after-leave="onAfterLeave"
@leave-cancelled="onLeaveCancelled"
>
<!-- ... -->
</Transition>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
export default {
// ...
methods: {
// 在元素被插入到 DOM 之前被调用
// 用这个来设置元素的 "enter-from" 状态
onBeforeEnter(el) {},

// 在元素被插入到 DOM 之后的下一帧被调用
// 用这个来开始进入动画
onEnter(el, done) {
// 调用回调函数 done 表示过渡结束
// 如果与 CSS 结合使用,则这个回调是可选参数
done()
},

// 当进入过渡完成时调用。
onAfterEnter(el) {},

// 当进入过渡在完成之前被取消时调用
onEnterCancelled(el) {},

// 在 leave 钩子之前调用
// 大多数时候,你应该只会用到 leave 钩子
onBeforeLeave(el) {},

// 在离开过渡开始时调用
// 用这个来开始离开动画
onLeave(el, done) {
// 调用回调函数 done 表示过渡结束
// 如果与 CSS 结合使用,则这个回调是可选参数
done()
},

// 在离开过渡完成、
// 且元素已从 DOM 中移除时调用
onAfterLeave(el) {},

// 仅在 v-show 过渡中可用
onLeaveCancelled(el) {}
}
}

在使用仅由 JavaScript 执行的动画时,最好是添加一个 :css="false" prop。这显式地向 Vue 表明可以跳过对 CSS 过渡的自动探测。除了性能稍好一些之外,还可以防止 CSS 规则意外地干扰过渡效果:

1
2
3
4
5
6
<Transition
...
:css="false"
>
...
</Transition>

可复用过渡效果

创建一个MyTransition.vue组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- MyTransition.vue -->
<script>
// JavaScript 钩子逻辑...
</script>

<template>
<!-- 包装内置的 Transition 组件 -->
<Transition
name="my-transition"
@enter="onEnter"
@leave="onLeave">
<slot></slot> <!-- 向内传递插槽内容 -->
</Transition>
</template>

<style>
/*
必要的 CSS...
注意:避免在这里使用 <style scoped>
因为那不会应用到插槽内容上
*/
</style>

scoped属性是HTML5中的新属性,是一个布尔属性。如果使用该属性,则样式仅仅应用到style元素的父元素及其子元素

现在 MyTransition 可以在导入后像内置组件那样使用了:

1
2
3
<MyTransition>
<div v-if="show">Hello</div>
</MyTransition>

出现时过渡

在某个节点初次渲染时应用一个过渡效果

1
2
3
<Transition appear>
...
</Transition>

元素间过渡

除了通过 v-if / v-show 切换一个元素,我们也可以通过 v-if / v-else / v-else-if 在几个组件间进行切换,只要确保任一时刻只会有一个元素被渲染即可:

1
2
3
4
5
<Transition>
<button v-if="docState === 'saved'">Edit</button>
<button v-else-if="docState === 'edited'">Save</button>
<button v-else-if="docState === 'editing'">Cancel</button>
</Transition>

过渡模式

在之前的例子中,进入和离开的元素都是在同时开始动画的,因此我们不得不将它们设为 position: absolute 以避免二者同时存在时出现的布局问题。

然而,很多情况下这可能并不符合需求。我们可能想要先执行离开动画,然后在其完成之后再执行元素的进入动画。手动编排这样的动画是非常复杂的,好在我们可以通过向 <Transition> 传入一个 mode prop 来实现这个行为:

1
2
3
<Transition mode="out-in">
...
</Transition>

组件间过渡

<Transition> 也可以作用于动态组件之间的切换:

1
2
3
<Transition name="fade" mode="out-in">
<component :is="activeComponent"></component>
</Transition>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<script>
import CompA from './CompA.vue'
import CompB from './CompB.vue'

export default {
components: { CompA, CompB },
data() {
return {
activeComponent: 'CompA'
}
}
}
</script>

<template>
<label>
<!-- v-model与activeComponent建立双向绑定 -->
<input type="radio" v-model="activeComponent" value="CompA"> A
</label>
<label>
<input type="radio" v-model="activeComponent" value="CompB"> B
</label>
<Transition name="fade" mode="out-in">
<component :is="activeComponent"></component>
</Transition>
</template>

<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.5s ease;
}

.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>

动态过渡

<Transition>props (比如 name) 也可以是动态的!这让我们可以根据状态变化动态地应用不同类型的过渡:

1
2
3
<Transition :name="transitionName">
<!-- ... -->
</Transition>

这个特性的用处是可以提前定义好多组 CSS 过渡或动画的 class,然后在它们之间动态切换。

使用 Key Attribute 过渡

有时为了触发过渡,需要强制重新渲染 DOM 元素。

比如计数器组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<script>
export default {
data() {
return {
count: 1,
interval: null
}
},
mounted() {
this.interval = setInterval(() => {
this.count++;
}, 1000)
},
beforeDestroy() {
clearInterval(this.interval)
}
}
</script>

<template>
<Transition>
<span :key="count">{{ count }}</span>
</Transition>
</template>

有了 key 属性,Vue 就知道在 count 改变时创建一个新的 span 元素,因此 Transition 组件有两个不同的元素在它们之间进行过渡

小结:

  1. 各种过渡都是说明可以有各种各样的目标对象。根据时间点,元素,组件,key值,name进行过渡操作

参考

  1. Vue-内置组件-Transition
  2. css 动画中 ease,seae-in,ease-in-out,ease-out,效果区别
  3. @keyframes
  4. vue—style scoped属性的作用和原理、scoped穿透
学习笔记 - Swfit 6.1 - 语法概览 Vue学习笔记 - 插件
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×