該系列是為了讓看過Vue官方文件或學過Vue但是卻不知道怎麼下手去重構現在有的網站而去規畫的系列文章,在這邊整理了許多我自己使用Vue重構很多網站的經驗分享給讀者們。
我們在開發網站的時候很常會處理 UI 的動態效果,以前大家會很常使用 JQuery 的 animate 的函式去做動態效果,但是 JQuery 的 animate 效能不是這麼好,再加上它只能使用在DOM上面,多少覺得有些不方便,所以今天要來介紹四種我自己在Vue上面開發網頁動態上面很常使用的方式。
在製作網頁的時候有許多小地方的動態其實可以不用透過JS的方式來處理,透過 CSS3 的 transition
還有 @keyframes
可以很輕易地達成,也好去把我們動畫的邏輯還有程式的業務邏輯給分開,我們來看一下這兩個的範例。
codepen 範例 https://codepen.io/MikeCheng1208/pen/NWgRGpp
<script>
import { ref } from 'vue'
export default {
setup(){
const isOpen = ref(false);
const handleMenuOpen = (bool) => {
isOpen.value = bool
}
return {
isOpen,
handleMenuOpen
}
}
};
</script>
<template>
<!-- 開啟選單的按鈕 -->
<a class="menuBtn" @click="handleMenuOpen(true)">
<i class="fas fa-bars fa-3x"></i>
</a>
<div class="content"></div>
<!-- 選單 -->
<div :class="['menu', {open: isOpen}]">
<!-- 關閉選單的按鈕 -->
<a class="closeBtn" @click="handleMenuOpen(false)">
<i class="fas fa-times fa-3x"></i>
</a>
<ul class="nav">
<li><a>abous</a></li>
<li><a>content</a></li>
<li><a>user</a></li>
<li><a>address</a></li>
</ul>
</div>
</template>
<style lang="scss">
#app{
// 以上省略...
.menu{
position: fixed;
top: 0;
right: -350px;
width: 350px;
height: 100%;
z-index: 20;
background-color: #fff;
// 對 right 屬性添加 transition 動畫
transition: right 0.3s;
&.open{
right: 0px;
}
// 以下省略...
}
}
</style>
我們在這邊使用了 css3 的 transition
屬性,因為我們的選單是用定位的方式去擺放位置,所以針對 right
這個屬性去處理,只要 right
一變動 transition
就會自動把改變的 value 中間給做上補間動畫。
關於 transition 的細節我們可以參考 MDN https://developer.mozilla.org/zh-TW/docs/Web/CSS/transition
不過要特別注意一件事情,很多人會為了貪圖方便,所以這樣寫
.menu{
// 對全部的屬性添加 transition 動畫
transition: all 0.3s;
&.open{
right: 0px;
}
}
用 all
的方式可以對 .menu
全部的屬性都給予動畫,但是這樣不是很好,因為我們並非全部都要使用 transition
,為了可以一眼就看出哪個屬性被賦予的動畫,也可以避免無謂的效能浪費,建議還是乖乖寫上單一屬性就好,如果要寫多個屬性的話可以這樣,用 ,
的方式串起來。
transition: right 0.3s, background 0.3s, color 0.3s;
codepen 範例 https://codepen.io/MikeCheng1208/pen/QWgKjVa
<script>
import { ref } from 'vue'
export default {
setup(){
const isError = ref(false);
const handleAlert = (bool) => {
isError.value = bool;
}
return {
isError,
handleAlert
}
}
};
</script>
<template>
<div class="content">
<button @click="handleAlert(true)">開啟Alert</button>
<div :class="['alert', {open: isError}]">
<i class="fas fa-times fa-6x" @click="handleAlert(false)"></i>
<h1>發生未知的錯誤</h1>
</div>
</div>
</template>
<style lang="scss">
@keyframes showAnim {
from {
bottom: -90%;
}
70% {
bottom: 70%;
}
to {
bottom: 50%;
}
}
#app{
// 以下省略 ...
.content{
// 以下省略 ...
.alert{
position: absolute;
bottom: -50%;
left: 50%;
transform: translateX(-50%) translateY(50%);
width: 500px;
height: 400px;
border-radius: 16px;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
box-shadow: 0 0 30px rgba(#000, 0.5);
&.open {
animation-name: showAnim;
animation-duration: 0.45s;
animation-iteration-count: 1;
animation-fill-mode: forwards;
animation-timing-function: ease-in-out;
}
}
}
}
</style>
關於 @keyframes 的細節我們可以參考 MDN https://developer.mozilla.org/zh-CN/docs/Web/CSS/@keyframes
這邊我們一樣是用切換 class 的方式來控制,只是這次的動畫需要更加的處理細節,你會看到我的 demo 有一個彈性的效果,像這樣的細節就可以使用 @keyframes
的方式來處理,再搭配 animation
屬性來處理,就可以很輕易的處理大部分網頁上的動態。
我們現在已經學會了使用 CSS3 來取代以前我們使用 JQuery 的 animate 的動態效果,透過 CSS3 的動畫,我們可以獲得更順暢的體驗,更好的管理動畫的邏輯。
除了透過切換 class 加入動畫以外,Vue 提供了一個 transition
的 component,讓我們可以用簡單的方式可以處理 component 之間的過渡動畫,我們只需要把你要執行動畫的 component 給包起來,透過 v-if
或是 v-show
就可以使用了。
<transition>
<div v-if="isShow"></div>
</transition>
以下就是我們用 transition
完成之後的樣子
codepen 範例 https://codepen.io/MikeCheng1208/pen/mdwrVRM
<script>
import { ref } from 'vue'
export default {
setup(){
const isShow = ref(false);
const handleAlert = (bool) => {
isShow.value = bool;
}
return {
isShow,
handleAlert
}
}
};
</script>
<template>
<div class="content">
<button @click="handleAlert(true)">開啟Alert</button>
<transition name="fade">
<div class="alert" v-if="isShow">
<i class="fas fa-times fa-6x" @click="handleAlert(false)"></i>
<h1>發生未知的錯誤</h1>
</div>
</transition>
</div>
</template>
<style lang="scss">
.fade-enter-active, .fade-leave-active {
transition: opacity 0.5s ease;
}
.fade-enter-from, .fade-leave-to {
opacity: 0;
}
// 以下省略...
</style>
你會看到我在 transition 身上給了一個 name
叫做 fade
,這個時候我就可以定義我的 transition 動畫在執行的時候它的 class 的定義,透過 v-if
或是 v-show
,來達到過渡的動態效果。
<transition name="mike"></transition>
// 這些 class 就是看你的 transition 的 name 叫什麼,前面的名稱就是你的 name
.mike-enter-active, .mike-leave-active {}
.mike-enter-from, .mike-leave-to {}
在 進場 到 離開 的過程中,會有 6 個 class 的狀態 ( 詳情請看官方文件 ),Vue2 跟 Vue3 的狀態名稱有稍微不一樣,所以看文件不要看錯。
v-enter-from
:開始的動畫開始前。v-enter-active
:動畫開始的執行過程。v-enter-to
:開始的動畫結束時。v-leave-from
:離開的動畫開始前。v-leave-active
:離開動畫的執行過程。v-leave-to
:離開動畫的結束時。既然我們已經知道了 transition 的 6 個狀態,那接下來我們來看一下這個範例。
這是一個用 transition
做出來的輪播效果,我們來看一下怎麼做
<script>
import { ref } from 'vue'
export default {
setup(){
const imgIdx = ref(0);
const slidList = ref([
{ id: '1', src: "https://source.unsplash.com/600x400?1" },
{ id: '2', src: "https://source.unsplash.com/600x400?2" },
{ id: '3', src: "https://source.unsplash.com/600x400?3" },
{ id: '4', src: "https://source.unsplash.com/600x400?4" },
{ id: '5', src: "https://source.unsplash.com/600x400?5" },
{ id: '6', src: "https://source.unsplash.com/600x400?6" },
{ id: '7', src: "https://source.unsplash.com/600x400?7" },
{ id: '8', src: "https://source.unsplash.com/600x400?8" },
]);
const handleMenuActive = idx => {
imgIdx.value = idx;
};
return {
imgIdx,
slidList,
handleMenuActive,
}
}
};
</script>
我們會先把圖片的資料 slidList
給定義出來,然後還有目前點到的按鈕索引 imgIdx
給定義出來。
<template>
<div class="content">
<div class="mid">
<transition name="slids">
<img
v-for="(item, idx) in slidList"
v-show="imgIdx === idx"
:key="item.id"
:src="item.src"
/>
</transition>
</div>
<nav class="nav_menu">
<a
v-for="(item, idx) in slidList"
:key="item.id"
:class="{active: imgIdx === idx}"
@click="handleMenuActive(idx)"
>
{{idx + 1}}
</a>
</nav>
</div>
</template>
<style lang="scss">
.slids-enter-active, .slids-leave-active{
transition: transform .3s ease;
}
.slids-enter-from{
transform: translateX(-550px);
}
.slids-leave-to{
transform: translateX(550px)
}
.content {
width: 600px;
height: 400px;
.mid{
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
margin-bottom: 20px;
img{
position: absolute;
top: 0;
right: 0;
}
}
}
// 其他省略...
</style>
然後對裡面的圖片去用 v-for
搭配 v-show
來處理切換的部分,在這邊要注意一下,圖片的部分是透過 position: absolute;
來定位,壓在同一個地方,接下來仔細的來看一下 css 的部分
<style lang="scss">
.slids-enter-active, .slids-leave-active{
transition: transform .3s ease;
}
.slids-enter-from{
transform: translateX(-550px);
}
.slids-leave-to{
transform: translateX(550px)
}
</style>
動畫開始的執行過程( slids-enter-active )
跟離開動畫的執行過程 (slids-leave-active)
都給它使用 transition
來處理過場的部分,然後每個圖片進來以前 slids-enter-from
,會被 transform 移到 -550px
這個位置 transform: translateX(-550px);
,自然進場的時候就會滑動到原本的地方,再來圖片離開的時候slids-leave-to
,就會被 transform 移到 550px
跑出去外面,所以只要能掌握這些狀態,你就用 transition 自由的控制你的組件要怎麼動。
但是現在會發生一個問題,就是使用 <transition></transition>
這個動畫組件,裡面的元件只能有一個,所以透過 v-for
迴圈產生出來的 <img/>
會無法執行,所以除了transition
這個動畫組件以外,還有另外一個叫做 transition-group
的組件,就是專門處理 v-for
迴圈產生出來的 DOM 去跑動態。
關於 transition-group 的官方文件 : https://v3.vuejs.org/api/built-in-components.html#transition-group
transition-group
跟transition
的 API 一模一樣,所以不用擔心要改其他東西。
<transition-group name="slids">
<img
v-for="(item, idx) in slidList"
v-show="imgIdx === idx"
:key="item.id"
:src="item.src"
/>
</transition-group>
這樣一來就可以正常的執行了。
codepen 範例 : https://codepen.io/MikeCheng1208/pen/oNwzxBq
因為我們現在只能朝一個方向滑動,當我今天希望我點擊的時候可以左右滑動,像是下面範例這樣。
所以我必須要去判斷說,我最新點擊的按鈕跟上一個按鈕比,是在左邊還是右邊,然後我要去切換 transition-group
的 name
,來達到動畫切換左右的部分。
首先我先定義一下左右滑的 css
<style lang="scss">
.left-enter-active,
.left-leave-active,
.right-enter-active,
.right-leave-active {
transition: transform .3s ease;
}
.left-enter-from{
transform: translateX(-550px);
}
.left-leave-to{
transform: translateX(550px)
}
.right-enter-from{
transform: translateX(550px)
}
.right-leave-to{
transform: translateX(-550px);
}
</style>
你會看到我的 class 開頭被改成了 left
跟right
,所以可想而知,我們的 transition-group
的 name
要切換 left
跟right
。
所以在這邊我新定義了兩個東西
const prevIdx = ref(0); // 紀錄上一個點擊的按鈕索引
const transType = ref("right"); // 當前的 transition-group 的 name
然後在我們點擊按鈕的 function 加入一下判斷
const handleMenuActive = idx => {
imgIdx.value = idx;
transType.value = idx > prevIdx.value ? "left" : "right";
}
這邊我會判斷當我點擊的索引比上一個索引大的時候,那就是往左滑,不然就右滑,但問題來了,我要什麼時候去紀錄上一個索引的值呢? 不能在 click 的時候,不然會同步的去寫入,所以有人會說,那等它慢一點在寫入,所以寫一個 setTimeout 就好了,拜託不要XDDDDD
transition
跟 transition-group
有提供動畫的 Lifecycle Hooks,所以這次我們要使用 after-leave
這個 hooks,after-leave
是當你動畫執行結束之後會觸發,所以我們要在 after-leave
的時候去寫入我們的 prevIdx.value
。
const afterLeave = () => {
prevIdx.value = imgIdx.value;
}
return {
afterLeave,
}
定義好了 prevIdx.value
的 callback 的 function 後,直接 on 這個 hooks
<transition-group
:name="transType"
@after-leave="afterLeave"
>
<img
v-for="(item, idx) in slidList"
v-show="imgIdx === idx"
:key="item.id"
:src="item.src"
/>
</transition-group>
這樣一來就可以讓你在 click 的時候,去切換 transition-group
的 name,以達到左右換圖的效果。
<script>
import { ref } from 'vue'
export default {
setup(){
const imgIdx = ref(0);
const prevIdx = ref(0);
const transType = ref("right");
const slidList = ref([
{ id: '1', src: "https://source.unsplash.com/600x400?1" },
{ id: '2', src: "https://source.unsplash.com/600x400?2" },
{ id: '3', src: "https://source.unsplash.com/600x400?3" },
{ id: '4', src: "https://source.unsplash.com/600x400?4" },
{ id: '5', src: "https://source.unsplash.com/600x400?5" },
{ id: '6', src: "https://source.unsplash.com/600x400?6" },
{ id: '7', src: "https://source.unsplash.com/600x400?7" },
{ id: '8', src: "https://source.unsplash.com/600x400?8" },
]);
const handleMenuActive = idx => {
imgIdx.value = idx;
transType.value = idx > prevIdx.value ? "left" : "right";
}
const afterLeave = () => {
prevIdx.value = imgIdx.value;
}
return {
imgIdx,
slidList,
handleMenuActive,
afterLeave,
transType
}
}
};
</script>
<template>
<div class="content">
<div class="mid">
<transition-group
:name="transType"
@after-leave="afterLeave"
>
<img
v-for="(item, idx) in slidList"
v-show="imgIdx === idx"
:key="item.id"
:src="item.src"
/>
</transition-group>
</div>
<nav class="nav_menu">
<a
v-for="(item, idx) in slidList"
:key="item.id"
:class="{active: imgIdx === idx}"
@click="handleMenuActive(idx)"
>
{{idx + 1}}
</a>
</nav>
</div>
</template>
<style lang="scss">
.left-enter-active,
.left-leave-active,
.right-enter-active,
.right-leave-active {
transition: transform .3s ease;
}
.left-enter-from{
transform: translateX(-550px);
}
.left-leave-to{
transform: translateX(550px)
}
.right-enter-from{
transform: translateX(550px)
}
.right-leave-to{
transform: translateX(-550px);
}
// 其他省略...
</style>
codepen 範例 : https://codepen.io/MikeCheng1208/pen/eYRdzVL
所以我們在處理動畫的時候不要再依賴 JQuery 的 animate 了,在使用 Vue 上面有更多好用的方式可以處理動態效果。
好啦 ! 今天篇幅也夠長了,先介紹兩個做法,明天我們在來討論另外兩個動畫的處理方式。
Ps. 購買的時候請登入或註冊該平台的會員,然後再使用下面連結進入網站點擊「立即購課」,這樣才可以讓我獲得更多的課程分潤,還可以幫助我完成更多豐富的內容給各位。
我有開設了一堂專門針對Vue3從零開始教學的課程,如果你覺得不錯的話,可以購買我課程來學習
https://hiskio.com/bundles/9WwPNYRpz?s=tc
那如果對於JS基礎不熟的朋友,我也有開設JS的入門課程,可以參考這個課程
https://hiskio.com/bundles/b9Rovqy7z?s=tc
Mike 的 Youtube 頻道
Mike的medium
MIke 的官方 line 帳號,好友搜尋 @mike_cheng