第5章 過渡與動畫
目錄
5.1 單元素/組件過渡
5.1.1 過渡class
5.1.2 CSS 過渡
5.1.3 CSS 動畫
5.1.4 同時使用過渡和動畫
5.1.5 JavaScript 鉤子方法
5.2 多元素/組件過渡
5.2.1 多元素過渡
5.2.2 多組件過渡
5.3 列表過渡
5.3.1 列表的普通過渡
5.3.2 列表的平滑過渡
5.3.3 列表的變換過渡
5.3.4 多維列表的過渡
5.1 單元素/組件過渡
Vue在插入、更新或者移除 DOM 時,提供多種不同方式的過渡效果(一個淡入淡出的效果)。在條件渲染(使用v-if)、條件展示(使用v-show)、動態組件、組件根節點等情形中,可以給任何元素和組件添加進入/離開過渡。Vue提供了內置的過渡封裝組件<transition>,該組件用于包裹要實現過渡效果的組件。具體語法如下:
<transition name = "過渡名稱">
? <!--要實現過渡效果的組件-->
</transition>
5.1.1 過渡class
總結起來就分為?進入?和?離開?動畫的?初始狀態、生效狀態、結束狀態,具體如下:
v-enter-from
- 進入?動畫的?起始狀態
- 在元素插入之前添加,在元素插入完成后的?下一幀移除
v-enter-active
- 進入?動畫的?生效狀態,應用于整個進入動畫階段
- 在元素被插入之前添加,在過渡或動畫完成之后移除
- 這個?
class
?可以被用來定義進入動畫的持續時間、延遲與速度曲線類型
v-enter-to
- 進入?動畫的?結束狀態
- 在元素插入完成后的下一幀被添加 (也就是?
v-enter-from
?被移除的同時),在過渡或動畫完成之后移除
v-leave-from
- 離開?動畫的?起始狀態
- 在離開過渡效果被觸發時立即添加,在一幀后被移除
v-leave-active
- 離開?動畫的?生效狀態,應用于整個離開動畫階段
- 在離開過渡效果被觸發時立即添加,在?過渡或動畫完成之后移除
- 這個?
class
?可以被用來定義離開動畫的持續時間、延遲與速度曲線類型
v-leave-to
- 離開?動畫的?結束狀態
- 在一個離開動畫被觸發后的?下一幀?被添加 (即?
v-leave-from
?被移除的同時),在?過渡或動畫完成之后移除
其中的?v
?前綴是允許修改的,可以?<Transition>
?組件傳一個?name
?的?prop
?來聲明一個過渡效果名,如下就是將?v
?前綴修改為?modal
?前綴:
<div id="app"><button @click="show = !show">點我,我就漸漸的離開、漸漸的來。</button><transition name="fade"><p v-show="show" :style="styleobj">動畫實例</p></transition>
</div>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {show: true,styleobj: {fontSize: '30px',color: 'red'}}}})app.mount('#app')
</script>
<style>.fade-enter-active,.fade-leave-active {transition: opacity 2s ease;}.fade-enter-from,.fade-leave-to {opacity: 0;}
</style>
5.1.2 CSS 過渡
常用的過渡一般都是CSS過渡。CSS過渡,顧名思義也就是使用過渡class定義CSS實現過渡效果。
【例5-2】使用過渡class定義圖片的進入和離開動畫效果,運行效果如圖5.2所示。
?
<div id="app"><button @click="show = !show">切換顯示圖片</button><transition name="slide-img"><p v-if="show"><img src="99.jpg"/></p></transition>
</div>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {show: true}}})app.mount('#app')
</script>
<style>.slide-img-enter-active { transition: all 5s ease-out; //ease-out 規定以慢速結束的過渡,進入界面需要延遲5秒才會顯示整個圖片}.slide-img-leave-active {transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);}.slide-img-enter-from,.slide-img-leave-to { transform: rotateX(45deg); //X軸45°旋轉transform: rotateY(45deg); transform: rotateZ(45deg); transform: rotate3d(1, 1, 1, 45deg);opacity: 0;}
</style>
5.1.3 CSS 動畫
CSS動畫與CSS過渡用法相同,區別是在動畫中v-enter類名在節點插入DOM后不會立即被刪除,而是在animationend(動畫結束)事件觸發時刪除。
1. 基本概念
CSS 動畫由兩個主要部分組成:@keyframes:定義動畫的關鍵幀,指定動畫在不同時間點的狀態。
? ? ?animation:應用于元素的屬性,控制動畫的播放、持續時間、延遲等。
2. @keyframes 關鍵幀的定義
@keyframes 定義了動畫的各個關鍵幀,控制元素的從一個狀態到另一個狀態的轉換過程。
【例5-3】使用@keyframes定義圖片的動畫規則,運行效果如圖5.3所示。
<div id="app"><button @click="show = !show">切換圖片動畫</button><transition name="bounce-img"><p v-if="show"><img src="99.jpg" /></p></transition>
</div>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {show: true}}})app.mount('#app')
</script>
<style>.bounce-img-enter-active {animation: bounce-in 0.8s;}.bounce-img-leave-active {animation: bounce-in 0.8s reverse;}@keyframes bounce-in {0% {transform: scale(0);}50% {transform: scale(1.25);}100% {transform: scale(1);}}
</style>
?
5.1.4 同時使用過渡和動畫
在一些應用場景中,需要給一個元素同時設置過渡和動畫,比如 animation 很快的被觸發并完成,而 transition 效果還沒結束。這時,需要使用 type屬性并設置 animation 或 transition值來明確聲明需要Vue監聽的類型。
在<transition> 組件的 duration prop上可顯性定義過渡持續時間(以毫秒計),比如,定制進入和移出的持續時間:<transition :duration="{ enter:1000, leave: 2000 }">...</transition>。還可以通過appear屬性設置DOM節點在初始渲染的過渡(頁面加載的初次過渡動畫):<transition appear>...</transition>。
<head><!--使用第三方動畫庫--><link rel="stylesheet" type="text/css" href="https://cdn.bootcss.com/animate.css/3.7.2/animate.min.css">
</head>
<!--使用type='transition'以transition過渡時長為準,
即fade-enter-active和fade-leave-active定義的時長,
或者綁定屬性:duration="{enter:5000,leave:5000}"自定義時長,
type='transition'與:duration="5000" 二選一;
使用animate.css必須使用vue的 enter-active-class 和 leave-active-class,
后面緊跟animated類名和想使用的動畫名稱;
appear,appear-active-class實現頁面加載的初次動畫(初始渲染過渡)。-->
<div id="app"><transition name='fade' appear :duration="{enter:5000,leave:5000}"enter-active-class='animated swing fade-enter-active' leave-active-class='animated shake fade-leave-active'appear-active-class='animated swing' ><div v-if='show'>hello world</div></transition><button @click="handleClick">切換顯示</button>
</div>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {show: true}},methods: {handleClick() {this.show = !this.show}}})app.mount('#app')
</script>
<style type="text/css">.fade-enter,.fade-leave-to {opacity: 0;}.fade-enter-active,.fade-leave-active {transition: opacity 5s;}div {font-size: 40px;margin: 50px auto;}
</style>
5.1.5 JavaScript 鉤子方法
?
<div id="app"><button @click="show = !show" class="btn">添加到購物車</button><transition @before-enter="beforeEnter" @enter="enter" @after-enter="afterEnter"><div v-if="show" class="ball"></div></transition>
</div>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {show: false}},methods: {//el表示將要執行動畫的DOM元素,beforeEnter表示動畫開始前beforeEnter(el) {//設置元素動畫開始前的起始位置el.style.transform = "translate(0, 0)"},enter(el, done) {//返回元素的寬度,強制動畫刷新el.offsetWidth//設置元素動畫開始之后的樣式,設置完成之后狀態el.style.transform = "translate(200px, -200px)"el.style.transition = "all 3s cubic-bezier(0, 0.54, 0.55, 1.18)"//done這里代表afterEnter方法的引用done()},afterEnter(el) {//動畫完成后,調用該方法this.show = !this.show}}})app.mount('#app')
</script>
<style>.ball {width: 30px;height: 30px;border-radius: 50%;background-color: green;position: absolute;z-index: 99;top: 200px;left: 100px;}.btn {position: absolute;top: 200px;}
</style>
5.2 多元素/組件過渡
5.2.1 多元素過渡
對于原生元素可以使用v-if/v-else實現多元素過渡。最常見的多元素過渡是一個列表和描述這個列表為空消息的元素:
<transition>
? <table v-if="items.length > 0">
? <!-- ... -->
? </table>
? <p v-else>沒有列表內容</p>
</transition>
實際上,通過使用多個v-if或將單個元素綁定到一個動態屬性上,可以在任意數量的元素之間進行過渡。
<div id="app"><button @click="handleClick('saved')">顯示Edit</button> <button @click="handleClick('edited')">顯示Save</button> <button @click="handleClick('editing')">顯示Cancel</button><br><br><transition name="fade" mode="out-in"><!--這里使用key讓vue區分相同標簽名元素,觸發過渡--><button :key="docState">{{ buttonMessage }}</button></transition>
</div>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {docState: ''}},methods: {handleClick(myVal) {this.docState = myVal}},computed: {buttonMessage() {switch (this.docState) {case 'saved': return 'Edit'case 'edited': return 'Save'case 'editing': return 'Cancel'default: return '初始按鈕'}}}})app.mount('#app')
</script>
<style type="text/css">.fade-enter-from,.fade-leave-to {opacity: 0;}.fade-enter-active,.fade-leave-active {transition: opacity 5s ease;}
</style>
5.2.2 多組件過渡
可以使用多組件過渡將多個組件包裝成動態組件的效果。
【例5-7】設計一個類似于選項卡的頁面,單擊“多組件過渡按鈕”將 “登錄子組件”和“注冊子組件”進行切換。
<div id="app"><button @click="show">多組件過渡按鈕</button><transition name="check" mode="out-in"><!-- is用來展示的template組件,mode組件切換的模式,name過渡的前綴,component占位符表示展示的組件 --><component :is="view"></component></transition>
</div>
<!-- 登錄子組件 -->
<template id="login"><div><h1>登錄子組件</h1></div>
</template>
<!-- 注冊子組件 -->
<template id="register"><div><h1>注冊子組件</h1></div>
</template>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {view: 'login'}},methods: {show: function () {if (this.view == "login") {this.view = "register"} else {this.view = "login"}}},components: {login: {template: "#login"},register: {template: "#register"}}})app.mount('#app')
</script>
<style type="text/css">.check-enter-from,.check-leave-to {opacity: 0;}.check-enter-active,.check-leave-active {transition: opacity 5s ease;}
</style>
5.3 列表過渡
對于列表元素,可使用<transition-group>組件進行過渡。<transition-group>組件具有以下幾個特點:
1、不同于 <transition>組件,它默認以<span>元素渲染。
2、過渡模式不可用,因為不再相互切換特有的元素。
3、內部元素需要提供唯一的key屬性值。
4、CSS過渡類將會應用在組件內部的元素中,而不是組件本身。
5.3.1 列表的普通過渡
5.3.2 列表的平滑過渡
在例5-8中,當添加和移除元素時,周圍元素將瞬間移動到它們的新布局位置,而不是平滑的過渡。
<transition-group> 組件不僅可以進入和離開列表過渡,還可以通過v-move特性改變定位,進行平滑過渡。v-move特性像之前的類名一樣,可以通過name屬性來自定義前綴。
<div id="list-demo" class="demo"><button @click="add">添加元素</button> <button @click="remove">移除元素</button><transition-group name="list" tag="p"><span v-for="item in items" :key="item" class="list-item">{{item}}</span></transition-group>
</div>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {items: [1, 2, 3, 4, 5, 6, 7, 8, 9],nextNum: 10}},methods: {randomIndex() {return Math.floor(Math.random() * this.items.length)},add() {this.items.splice(this.randomIndex(), 0, this.nextNum++)},remove() {this.items.splice(this.randomIndex(), 1)}}})app.mount('#list-demo')
</script>
<style>.list-item {display: inline-block;margin-right: 10px;}.list-enter-active,.list-leave-active {transition: all 3s;}.list-enter,.list-leave-to {opacity: 0;transform: translateY(30px);}
</style>
【例5-9】列表的平滑過渡,可以在例5-8的基礎上,做出如下改進:1、增加.list-move樣式,使元素在進入時實現過渡效果;2、在.list-leave-active中設置絕對定位,使元素在離開時實現過渡效果。
<div id="list-demo" class="demo"><button @click="add">添加元素</button> <button @click="remove">移除元素</button><transition-group name="list" tag="p"><span v-for="item in items" :key="item" class="list-item">{{item}}</span></transition-group>
</div>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {items: [1, 2, 3, 4, 5, 6, 7, 8, 9],nextNum: 10}},methods: {randomIndex() {return Math.floor(Math.random() * this.items.length)},add() {this.items.splice(this.randomIndex(), 0, this.nextNum++)},remove() {this.items.splice(this.randomIndex(), 1)}}})app.mount('#list-demo')
</script>
<style>.list-item {display: inline-block;margin-right: 10px;}.list-move,.list-enter-active,.list-leave-active {transition: 3s;}.list-leave-active {position: absolute;}.list-enter,.list-leave-to {opacity: 0;transform: translateY(30px);}
</style>
5.3.3 列表的變換過渡
利用move屬性,進行變換過渡,即一個列表中的列表項既不增加也不減少,只是不斷地變換其位置。
【例5-10】列表的變換過渡。
<div id="list-demo" class="demo"><button @click="shuffle">變換過渡</button><transition-group name="list" tag="ul"><li v-for="item in items" :key="item">{{item}}</li></transition-group>
</div>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {items: [1, 2, 3, 4, 5, 6, 7, 8, 9],nextNum: 10}},methods: {shuffle() {this.items = this.items.sort(() => { return Math.random() - 0.5; })}}})app.mount('#list-demo')
</script>
<style>.list-move {transition: transform 3s;}
</style>
5.3.4 多維列表的過渡
FLIP動畫不僅可以實現單列表過渡,多維網格列表的過渡也同樣簡單。
【例5-11】多維網格列表的過渡。
<div id="list-demo" class="demo"><button @click="shuffle">多維列表變換</button><transition-group name="cell" tag="div" class="container"><span v-for="cell in cells" :key="cell.id" class="cell">{{ cell.number }}</span></transition-group>
</div>
<script src="js/vue.global.js"></script>
<script>const app = Vue.createApp({data() {return {cells: Array.apply(null, { length: 81 }).map(function (_, index) {return {id: index,number: index % 9 + 1}})}},methods: {shuffle() {this.cells = this.cells.sort(() => { return Math.random() - 0.5; })}}})app.mount('#list-demo')
</script>
<style>.container {width: 270px;margin-top: 10px;line-height: 30px;text-align: center;}.cell {display: inline-block;width: 30px;height: 30px;outline: 1px solid #aaa;}.cell-move {transition: 3s;}
</style>