Vue.js 可以在新增、更新或移除 DOM 時使用 CSS 顯示過場效果及動畫,讓元素或元件產生漸進變化。動畫效果可以自己設定,也可以使用第三方的函式庫。
Vue 提供了對 CSS transition
過場屬性封裝的元件,可以讓元素或元件顯示「進入」和「離開」的過場效果或動畫。
使用方法
Vue 提供 6 個特殊的class
名稱:
v-enter
:元素一開始的狀態。在元素被新增時觸發,在下一個影格立即移除。v-enter-active
:元素被新增時的狀態。在元素被新增前加入,然後在整個動畫中使用它,最後在動畫結時被移除。v-enter-to
:元素新增狀態的結束。在元素被新增後觸發,在v-enter-active
動畫結束後被移除。這是 v2.1.8 新增的狀態,原有的v-enter
被它取代。v-leave
:元素被刪除前的初始狀態。在刪除時立即觸發,在下一個影格立即移除。v-leave-active
:元素被刪除時的狀態。在元素被移除前加入,然後在整個動畫中使用它,最後在動畫結束時被移除。v-leave-to
:元素刪除狀態的結束。在元素被刪除後觸發,在v-leave-active
動畫結束後移除。這是 v2.1.8 新增的狀態,原有的v-leave
被它取代。
簡單的說 Enter 的部分
v-enter
是頭 (0),v-enter-to
是尾 (1),那 v-enter-active
就是從頭到尾 (0~1)。這裡的 0 及 1 是指你所設定的數值,這裡舉例透明度為 0 ~ 1。Leave 的行為和 Enter 相反,當然你可以自行指定,只是通常來說,會讓頭尾相接,形成一個循環。當你要對某個元素/元件加入過場效果或動畫時,請用 Vue 提供的
<transition name="xxx">
來包住這個元素,記住!一個 <transition>
只能包含一個根元素/元件,如果有多個元素,必須將它們放在根元素之下,並且 v-if
及 v-show
只能放在這個根元素或元件上。其中的
name="xxx"
屬性,它會用來配對 CSS 中以它為開頭的特殊 class 名稱,例如 v-enter
會配對 xxx-enter
,也就是說,v
這個前置字會被換成這個 name
的值。
CSS 過場效果
依照前面的使用方法,直接來看範例:HTML<div id="vm">
<transition name="fade">
<div v-show="show">
<h1>Hello~</h1>
</div>
</transition>
</div>
<script>
new Vue({
el: '#vm',
data: {
show: true
},
mounted: function () {
var self = this;
setInterval(function () {
self.show = !self.show;
}, 1000);
}
});
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: opacity .8s;
}
.fade-enter,
.fade-leave-to {
opacity: 0;
}
</style>
執行結果:
你也可以使用變形 transform
來做更炫的效果 (程式碼無須更動):
CSS.fade-enter-active,
.fade-leave-active {
transition: all .8s ease;
}
.fade-enter {
transform: translateX(50px);
opacity: 0;
}
.fade-leave-to {
transform: translateX(-50px);
opacity: 0;
}
執行結果:
CSS 動畫
你也可以使用 CSS 動畫:CSS.bounce-enter-active {
animation: bounce-in .5s;
}
.bounce-leave-active {
animation: bounce-in .5s reverse;
}
@keyframes bounce-in {
0% {
transform: scale(0);
}
50% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
HTML 的部份改成:
<transition name="bounce">
執行結果:
自訂過場 Class
除了 Vue 提供的那 6 個 class ,你也可自定,一樣是 6 個:enter-class
enter-active-class
enter-to-class
(v2.1.8 新增)leave-class
leave-active-class
leave-to-class
(v2.1.8 新增)
HTML<!-- 在 head 中加入 -->
<link href="https://unpkg.com/animate.css@3.5.2/animate.min.css" rel="stylesheet" type="text/css">
<!-- 修改如下 -->
<transition
name="my-transition"
enter-active-class="animated rollIn"
leave-active-class="animated zoomOutUp">
...略
執行結果:
在 2 個元素間過場
你可以在使用v-if
、v-else
時同時讓兩個元素產生過場效果:
HTML<div id="vm">
<transition name="fade">
<div v-if="show">
<h1>Hello~</h1>
</div>
<p v-else>
哈囉~
</p>
</transition>
</div>
執行結果:
效果怪怪的,因為兩個元素同時執行過場時,會佔用另一個元素的空間,Vue 提供了解決方法,只要加上 mode
屬性即可,它有 2 個值:
in-out
:新元素先執行過場進入,完成後目前元素才執行過場離開。out-in
:目前元素先執行過場離開,完成後新元素才執行過場進入。這個值比較常用。
<transition name="fade" mode="out-in">
執行結果:
這裡有一點要注意, v-if
、v-else
用在兩個不同的元素上,如前例是可以執行的,但如果用在相同類型的元素上,就必須額外加上 key
屬性讓 Vue 可以區別:
HTML<transition name="fade" mode="out-in">
<p v-if="show" key="en">
Hello~
</p>
<p v-else key="tw">
哈囉~
</p>
</transition>
針對大量元素的過場
前面的例子都是針對單一元素,由v-if
或 v-show
來控制的元素,如果要針對由 v-for
產生的大量元素,就要改為使用 <transition-group>
元件。<transition-group>
元件預設會有一個 <span>
的根元素,它會包住由 v-for
產生的多個元素當成其子元素。你可以改變這個預設的根元素,加上一個 tag
屬性即可,例如:
HTML<transition-group name="fade">
<p v-for="...略" :key="...略">...略</p>
</transition-group>
瀏覽器會看到:
<span>
<p></p>
<p></p>
...略
</span>
指定 tag
屬性後:
HTML<transition-group name="fade" tag="div">
<p v-for="...略" :key="...略">...略</p>
</transition-group>
瀏覽器會看到:
<div>
<p></p>
<p></p>
...略
</div>
另外,規則和 <transition>
一樣,只要是相同元素就必須提供 key
,所以由 v-for
產生的子元素全都要加上 key
屬性。key 屬性的值必須為數字或字串,否則 Vue 會發出警告訊息。使用範例:
HTML<div id="vm">
<transition-group name="fade" tag="div">
<span v-for="n in numbers" :key="n">{{ n }} </span>
</transition-group>
</div>
<script>
new Vue({
el: '#vm',
data: {
numbers: [1,2,3,4,5],
next: 6,
flow: 1
},
methods: {
randomIndex: function () {
return Math.floor(Math.random() * this.numbers.length)
},
insert: function () {
var r = this.randomIndex();
this.numbers.splice(r, 0, this.next++);
},
remove: function () {
var r = this.randomIndex();
this.numbers.splice(r, 1);
this.next--;
},
timer: function () {
var len = this.numbers.length;
if (len >= 9) {
this.flow = -1;
} else if (len <= 1) {
this.flow = 1;
}
//
if (this.flow > 0) {
this.insert();
} else {
this.remove();
}
}
},
mounted: function () {
setInterval(this.timer, 1000);
}
});
</script>
<style>
.fade-enter-active,
.fade-leave-active {
transition: all .5s ease;
}
.fade-enter {
transform: translateX(50px);
opacity: 0;
}
.fade-leave-to {
transform: translateX(-50px);
opacity: 0;
}
</style>
執行結果:
你可能有發現效果怪怪的,當元素被加入或移除時,旁邊的其他元素會立即被擠開或被佔住位置,感覺像是瞬間跳到定位而不是漸漸到位。要解決這個問題,要在子元素中加入針對它的 CSS,修改後如下:
...略
<span v-for="n in numbers" :key="n" class="fade-item">...略
CSS 的部份改成:
CSS.fade-item {
transition: all .5s;
display: inline-block;
margin-right: 5px;
}
.fade-enter-active,
.fade-leave-active {
transition: all .5s ease;
}
.fade-leave-active {
position: absolute;
}
.fade-enter {
transform: translateX(50px);
opacity: 0;
}
.fade-leave-to {
transform: translateX(-50px);
opacity: 0;
}
執行結果:
這裡必須注意的是,子元素不能能設定為 display: inline
,可以用 display: inline-block
來代替。
建立可重覆使用的過場效果
方法很簡單,就是使用元件,這個元件的根元素就是<transition>
或 <transition-group>
,並使用插槽 <slot>
就可以了,例如:
HTML<div id="vm">
<list>
<span v-for="n in numbers" :key="n" class="fade-item">{{ n }} </span>
</list>
</div>
<script>
Vue.component('list', {
template: '<transition-group name="fade" tag="div">\
<slot></slot>\
</transition-group>'
})
new Vue({...略
</script>
以 Vue.js (12) - 實戰 CRUD 使用 Laravel + Vue 這一篇來舉例:
1. 建立 List.vue
透過 Vue.js (13) - 建立 Laravel Artisan 指令產生 Vue 元件檔 建立的指令,我們可以快速產生 List.vue,請執行:php artisan make:vue List --nojs
把前面範例中的內容搬進來:
HTML<template>
<transition-group name="fade" tag="div">
<slot></slot>
</transition-group>
</template>
<script>
export default {
data() {
return {
}
}
}
</script>
<style scoped>
.fade-enter-active,
.fade-leave-active {
transition: all .5s ease;
}
.fade-leave-active {
position: absolute;
}
.fade-enter {
transform: translateX(50px);
opacity: 0;
}
.fade-leave-to {
transform: translateX(-50px);
opacity: 0;
}
</style>
2. 修改 resources/assets/js/components/Post.vue:
<List id="list">
<div v-for="post in posts" :key="post.id" class="fade-item">
...略
</div>
</List>
用我們建立的 List
元件把文章清單包起來,記得加上 :key
屬性,接著註冊元件:
<script>
import List from './List';
export default {
components: {
List
},
// ...略
}
</script>
我們原本的做法是在新增、更新及刪除文章時,都直接重載全部的文章,這裡要稍做修改。
app/Http/Controllers/PostController.php
首先,修改PostController.php
,當新增成功時回傳該筆記錄:
function apiCreatePost(Request $request) {
// ...略
return response()->json(['ok' => $ok, 'thepost' => $post], 200);
}
接著回到 Post.vue
繼續修改。
發佈的部分
publish: function () {
// ...略
if (response.data['ok']) {
self.posts.push(response.data['thepost']);
// self.init();
// ...略
將原本收到新增成功時重新載入全部文章的動作,改成直接將新增成功的那一筆資料 push 到陣列中。
儲存的部份
save: function () {
// ...略
if (response.data['ok']) {
let i;
let len = self.posts.length;
for (i = 0; i < len; i++) {
if (self.posts[i].id == self.post.id) {
self.posts[i].title = self.post.title;
self.posts[i].body = self.post.body;
break;
}
}
// self.init();
// ...略
現在不在全部重載,只要更新本地端的資料即可。
刪除的部份
remove: function (id) {
// ...略
if (response.data['ok']) {
let i;
let len = self.posts.length;
for (i = 0; i < len; i++) {
if (self.posts[i].id == id) {
self.posts.splice(i, 1);
break;
}
}
// self.init();
}
現在不在全部重載,改為刪除本地資料陣列中的該項目。
CSS 的部份
CSS<style scoped>
.fade-item {
transition: all .5s ease;
}
.fade-item h1 {
margin-top: 0;
}
#list {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 240px;
padding: 20px;
margin: 20px 0;
overflow: scroll;
}
#form {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 20px;
box-shadow: 0 6px 20px 0 #333333;
}
</style>
為了讓過場效果正常,表單現在被固定在下方,文章清單在上方並且當文章過多時顯示捲動列。<h1>
在測試的時候因為有 margin-top
屬性,會影響過場位置的判斷,過場會怪怪的,所以將它的值為改為 0。這樣就完成了。記得執行自動打包指令
yarn run watch
,就能在瀏覽器上看到結果了。執行結果:
過場的其他做法
Vue.js 官方還有提供使用 JavaScript 的方式建立的過場效果,更加靈活,但也意謂著更加複雜,完整內容可以在官網 Transition Effects 文件中找到說明。
本文網址:https://blog.tonycube.com/2017/06/vuejs-14-transition-animation.html
由 Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀
由 Tony Blog 撰寫,請勿全文複製,轉載時請註明出處及連結,謝謝 😀
感謝分享 很詳盡好懂!
回覆刪除好棒!!!! 我是菜鳥前端正在不斷充實自己
回覆刪除