Vue 中 CSS 動畫原理
<div id="app">
<div v-if="show">hello world</div>
<button @click="handleClick">切換</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
現在希望 hello world 在隱藏和顯示的時候加上漸隱漸現的動畫效果。
在想要添加動畫的標籤外部添加 <transiton></transiton>
標籤,Vue 會自動幫助我們構建一個動畫的流程。
<transiton></transiton>
包裹的內容有一個過渡的動畫效果,可以通過 name 屬性給該標籤起一個名字。
- 從隱藏狀態變成顯示狀態:
- 從顯示狀態變成隱藏狀態:
fade-enter 中的前綴 fade 是 transition 中 name 定義的名字。如果 transition 沒有定義 name 屬性,前綴默認爲 v。即:v-enter,v-enter-active,……
<head>
<style>
.fade-enter, .fade-leave-to {
opacity: 0;
}
.fade-enter-active, .fade-leave-active {
transition: opacity 3s
}
</style>
</head>
<body>
<div id="app">
<transition name="fade">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切換</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
</body>
通過在某一時刻自動向 div 元素上增加一些 class 的底層原理,Vue 幫助我們實現了 CSS 過渡動畫效果。
在 Vue 中使用 animate.css 庫
使用 @keyframes
實現動畫效果
<head>
<style>
@keyframes bounce-in {
0% {
transform: scale(0)
}
50% {
transform: scale(1.5)
}
100% {
transform: scale(1)
}
}
.fade-enter-active {
transform-origin: left center;
animation: bounce-in 1s;
}
.fade-leave-active {
transform-origin: left center;
animation: bounce-in 1s reverse;
}
</style>
</head>
<body>
<div id="app">
<transition name="fade">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切換</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
</body>
不使用 transition 標籤默認提供的類名
當使用 transition 標籤的時候,設置了 name=“fade”,它就會自動添加 fade-enter-active、fade-leave-active 這樣子的 class 的名字。
如果我想自定義 class,不用默認提供的命名規範可以嗎?
可以,只需要在 transition 外面自定義 class 名字:
<transition name="fade" enter-active-class="active" leave-active-class="leave">
<div v-if="show">hello world</div>
</transition>
.active {
transform-origin: left center;
animation: bounce-in 1s;
}
.leave {
transform-origin: left center;
animation: bounce-in 1s reverse;
}
使用 animate.css 庫
<head>
<!-- …… -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css">
</head>
<body>
<div id="app">
<transition name="fade" enter-active-class="animate__animated animate__swing"
leave-active-class="animate__animated animate__shakeY">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切換</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
</body>
-
引入 animate.css 動畫庫(教程:https://animate.style/#usage)
複雜的動畫不再需要自己去寫,直接用庫裏提供的動畫效果即可。
-
必須使用自定義 class 名字的形式來使用 animate.css
-
class 類裏面必須包含
animate__animated
這樣一個具體的類,然後根據需求不同,把動畫效果的名字寫在第二個類名的位置。(enter-active-class="animate__animated animate__swing"
)動畫效果的名字可以在官網進行復制(點擊下圖中的複製符號即可)
在 Vue 中同時使用過渡和動畫
如何在第一次加載顯示的時候也具備動畫的效果?
上面的例子中,只是在點擊按鈕的時候觸發動畫效果。如果想要在第一次加載顯示的時候也具備動畫的效果,該如何去做?
增加一個自定義的 class 名字 appear-active-class
(也要同時增加 appear
屬性):
<transition
name="fade"
appear
enter-active-class="animate__animated animate__swing"
leave-active-class="animate__animated animate__shakeY"
appear-active-class="animate__animated animate__swing"
>
<div v-if="show">hello world</div>
</transition>
同時使用過渡和動畫
當 @keyframe 形式的動畫 (animate__swing) 執行的是 1s,transition 動畫執行的時間是 3s 時,整個動畫效果會按哪個時間顯示呢?
可以手動設置:(type="transition"
)
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css">
<style>
.fade-enter,
.fade-leave-to {
opacity: 0;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 3s
}
</style>
</head>
<body>
<div id="app">
<transition type="transition" name="fade" appear
enter-active-class="animate__animated animate__swing fade-enter-active"
leave-active-class="animate__animated animate__shakeY fade-leave-active"
appear-active-class="animate__animated animate__swing">
<div v-if="show">hello world</div>
</transition>
<button @click="handleClick">切換</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
</body>
上面是按照 transition 過渡動畫的時長作爲總時長,能否自定義動畫的時長呢?
自定義動畫的時長
<transition :duration="5000" name="fade" appear
enter-active-class="animate__animated animate__swing fade-enter-active"
leave-active-class="animate__animated animate__shakeY fade-leave-active"
appear-active-class="animate__animated animate__swing">
<div v-if="show">hello world</div>
</transition>
:duration="5000"
:5000 指的是毫秒。
點擊按鈕, 1s 動畫就執行完了,樣式依然存在,5s 後樣式纔會被清除。
:duration="{enter: 5000, leave: 10000}"
:出場動畫 10s 後被移除,入場動畫 5s 後 class 被清除。
Vue 中的 Js 動畫與 Velocity.js 的結合
上面的例子都是通過 CSS 實現的動畫效果,那有沒有辦法通過 JS 實現動畫效果呢?
有的,Vue 提供了一些 JS 動畫的鉤子。
JS 動畫
- 入場動畫
@before-enter
當元素即將顯示的一瞬間,Vue 會自動觸發其綁定的事件。@enter
當 @before-enter 被觸發結束之後,就要真正運行動畫。當真正開始運行動畫效果時,其所有的動畫會寫在 @enter 鉤子對應的回調函數裏面。@after-enter
當 done 被調用後,Vue 會觸發 @after-enter 事件。
- 出場動畫
@before-leave
@leave
@after-leave
<div id="app">
<transition name="fade" @before-enter="handleBeforeEnter" @enter="handleEnter" @after-enter="handleAfterEnter">
<div v-show="show">hello world</div>
</transition>
<button @click="handleClick">切換</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
},
handleBeforeEnter(el) { // el 指的是動畫包裹的div標籤 (當元素即將顯示的一瞬間,Vue會自動觸發handleBeforeEnter事件)
el.style.color = "red";
},
handleEnter(el, done) {
// 接收兩個參數:第一個參數 el,div的元素;第二個參數 done,回調函數 (當@before-enter被觸發結束之後,就要真正運行動畫,當真正開始運行動畫效果時,其所有的動畫會寫在@enter鉤子對應的回調函數handleEnter裏面)
setTimeout(() => {
el.style.color = "green";
// done(); 爲了使效果明顯一點,寫在下面:4s時讓動畫結束
}, 2000)
setTimeout(() => {
done(); // 當動畫結束時,要手動調用 done 這個回調函數,相當於告訴 Vue 動畫已經執行完了
}, 4000)
},
handleAfterEnter(el) { //當 done 被調用後,Vue 會觸發 @after-enter 事件
el.style.color = "black";
}
}
})
</script>
JS 常用動畫庫:Velocity.js
下載 velocity.js 後用 <script></script>
標籤引入。
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
},
handleBeforeEnter(el) {
el.style.opacity = 0;
},
handleEnter(el, done) {
Velocity(el, {opacity: 1}, {duration: 1000, complete: done})
},
handleAfterEnter(el) {
console.log("動畫結束")
}
}
})
Vue 中多個元素或組件的過渡動畫
多個元素的過渡動畫
.v-enter, .v-leave-to {
opacity: 0;
}
.v-enter-active, .v-leave-active {
transition: opacity 1s;
}
<div id="app">
<transition>
<div v-if="show" key="hello">hello world</div>
<div v-else key="bye">bye world</div>
</transition>
<button @click="handleClick">切換</button>
</div>
<script>
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
transition
提供了 mode
屬性:
<transition mode="in-out"> // 先顯示再隱藏
多個組件的過渡動畫
<div id="app">
<transition mode="in-out">
<child v-if="show"></child>
<child-one v-else></child-one>
</transition>
<button @click="handleClick">切換</button>
</div>
<script>
Vue.component('child', {
template: '<div>child</div>'
})
Vue.component('child-one', {
template: '<div>child-one</div>'
})
var vm = new Vue({
el: '#app',
data: {
show: true
},
methods: {
handleClick() {
this.show = !this.show;
}
}
})
</script>
- 也可以通過動態組件來實現:
<div id="app">
<transition mode="in-out">
<component :is="type"></component>
</transition>
<button @click="handleClick">切換</button>
</div>
<script>
Vue.component('child', {
template: '<div>child</div>'
})
Vue.component('child-one', {
template: '<div>child-one</div>'
})
var vm = new Vue({
el: '#app',
data: {
type: 'child'
},
methods: {
handleClick() {
this.type = this.type === 'child' ? 'child-one' : 'child';
}
}
})
</script>
Vue 中的列表過渡
<div id="app">
<div v-for="item of list" :key="item.id">
{{item.title}}
</div>
<button @click="handleBtnClick">Add</button>
</div>
<script>
var count = 0;
var vm = new Vue({
el: '#app',
data: {
list: []
},
methods: {
handleBtnClick() {
this.list.push({
id: count++,
title: 'hello world'
})
}
}
})
</script>
👆每點擊一個 Add 就會增加一條數據。
現在我們想往列表中增加內容時實現列表過渡的效果:
<transition-group></transition-group>
:相當於在每一個 div 外層都加了一個<transition></transition>
。<transition-group> <div v-for="item of list" :key="item.id"> {{item.title}} </div> </transition-group>
-
.v-enter, .v-leave-to { opacity: 0; } .v-enter-active, .v-leave-active { transition: opacity 1s }
如果能不用 index 作爲 key 值,就不要用!!!
Vue 中的動畫封裝
如果我們覺得某一個動畫效果用的比較多,可以對這個動畫效果進行封裝。
.v-enter, .v-leave-to {
opacity: 0;
}
.v-enter-active, .v-leave-active {
transition: opacity 1s
}
<div id="app">
<fade :show="show">
<div>hello world</div>
</fade>
<button @click="handleBtnClick">切換</button>
</div>
<script>
Vue.component('fade', {
props: ['show'],
template: `
<transition>
<slot v-if="show"></slot>
</transition>
`
})
var vm = new Vue({
el: '#app',
data: {
show: false
},
methods: {
handleBtnClick() {
this.show = !this.show;
}
}
})
</script>
👆 已經把動畫效果封裝到了 <fade></fade>
組件中,如果想再用這個動畫效果,可以直接複製 fade 組件的內容,傳不同的 dom 元素進去:
<fade :show="show">
<h1>hello world</h1>
</fade>
樣式也可一起封裝到動畫組件中。
不用 css 動畫,轉而使用 js 動畫:
Vue.component('fade', {
props: ['show'],
template: `
<transition @before-enter="handleBeforeEnter" @enter="handleEnter">
<slot v-if="show"></slot>
</transition>
`,
methods: {
handleBeforeEnter(el) {
el.style.color = "red";
},
handleEnter(el, done) {
setTimeout(() => {
el.style.color = "green";
done();
}, 2000)
}
}
})
可以完整的把所有的動畫的實現封裝在一個組件裏面。
更多 more: