為你的 React Apps 加上動畫效果
沒有動畫效果的 Web Apps 不是說不好,但就是在完整度上少了一點什麼。如果可以適當添加一些動畫效果,除了可以讓網站看起來更加用心,也可以吸引使用者持續瀏覽。本文會介紹前端加入動畫效果的幾種方式,包含 CSS Transitions、CSS Animations,以及 Framer Motion 這套動畫效果 Library。
介紹順序會從 CSS 開始,最後提到 Framer Motion 的使用。許多情況下,我們或許不需要使用到 Framer Motion,而是使用 CSS 內建的 Transitions 與 Animations 就可以完成我們想要達到的效果。
CSS Transitions
- CSS Transitions 可以讓你指定 CSS 屬性,讓它在兩個屬性值之間絲滑地切換
範例:旋轉圖標
CSS Transitions 常常被用在製作旋轉圖標上,許多下拉選單操作都會有一個三角形 ▲ 的圖標,我們可以為它加上 CSS 動畫效果,讓使用者點擊 ▲ 後會旋轉為一個倒三角形 ▼。
1// 假設已知我們有一個控制是否展開的狀態為 isExpanded 2<div className={`item-details ${isExpanded ? 'expanded' : ''}`}> 3 <button onClick={onViewDetails}> 4 View Details <span className="item-details-icon">▲</span> 5 </button> 6</div> 7
在 CSS 裡把 transition
屬性加到要旋轉的圖標上面,這裡不要加在 .item-details.expanded
,而是加在 .item-details-icon
上面,因為我們希望圖標不論是展開還是收合都能有旋轉的動畫效果。
1.item-details-icon { 2 display: inline-block; 3 font-size: 0.85rem; 4 margin-left: 0.25rem; 5 transition: transform 0.3s ease-out; /* CSS Transitions */ 6} 7 8.item-details.expanded .item-details-icon { 9 transform: rotate(180deg); 10} 11
CSS Animations
- CSS Animations 可以讓元素初始渲染時,執行自定義的
@keyframes
動畫
範例:卡片滑出效果
這個範例中,當 Modal 在關閉狀態時,Modal 並不存在於 DOM 之中,因此我們沒辦法使用 CSS Transitions 為它製作動畫效果,這種時候可以使用 CSS Animations 為 Modal 加上動畫。
1return ( 2 <> 3 isShowModal && <Modal onDone={handleDone} /> 4 </> 5); 6
當 Modal 開啟時,就會執行我們在 @keyframes
定義的 slide-up-fade-in
動畫效果,Modal 會從一開始的 opacity: 0
浮現到 1
,同時位置也會透過 translateY
從下方 30px
的位置往上移動到 0px
。最後的 forwards
則是指希望元素在動畫完成後,保留在動畫結束的狀態。
1.modal { 2 top: 10%; 3 border-radius: 6px; 4 padding: 1.5rem; 5 width: 30rem; 6 max-width: 90%; 7 z-index: 10; 8 animation: slide-up-fade-in 0.3s ease-out forwards; 9} 10 11@keyframes slide-up-fade-in { 12 0% { 13 transform: translateY(30px); 14 opacity: 0; 15 } 16 100% { 17 transform: translateY(0); 18 opacity: 1; 19 } 20} 21
Framer Motion
- 強大且效果自然的 React 動畫效果 Library
安裝 Framer Motion 之後,可以引入 motion
並且選用它包裝過後的 HTML 元素像是 motion.div
,準備完成後,我們就可以開始為元素加上動畫了。
1import { motion } from 'framer-motion'; 2 3function App() { 4 return <motion.div />; 5} 6
可以通過以下屬性,為元素加上動畫:
initial
:元素加入 DOM 時,為將要執行的動畫加上「初始」值animate
:指定要「執行」動畫的屬性與目標值exit
:從 DOM「刪除」該元素時,希望顯示的動畫效果transition
:動畫執行的細節設定,可以設置duration
、type
、stiffness
等等AnimatePresence
:延緩內部元件從 React Tree 中消失,等到動畫完成才真正地移除 DOMwhileHover
:whileXxx 定義的動畫效果,會在 Tapping 或者 Hovering 等時候執行variants
:建立變數方便複用效果
更多詳細內容可以參考 Framer Motion 官方文件
範例:位移效果
這個範例中,我們根據不同欄位的輸入值,讓方塊執行移動或旋轉動畫。
1import { motion } from 'framer-motion'; 2 3function App() { 4 const [x, setX] = useState(0); 5 const [y, setY] = useState(0); 6 const [rotate, setRotate] = useState(0); 7 8 return ( 9 <motion.div 10 animate={{ x y, rotate }} 11 transition={{ 12 duration: 0.5, 13 // debounce: 0 ~ 1, // Bouncing 程度 (0 = 無彈跳, 1 = 非常彈跳) 14 type: 'spring', // 'spring', 'tween' 15 }} 16 /> 17 ); 18} 19
範例:取代 CSS Animations 完成卡片滑出效果
這個範例我們透過 initial
與 animate
這兩個屬性,讓 Modal 加入到 DOM 之後,從初始值執行動畫到指定的目標值,以完成由下往上滑出卡片的動畫效果。
除此之外,我們還可以加上 exit
為 DOM 移除的動作也加上動畫效果。也因此還必須加上 <AnimatePresence>
來包裝這個 Modal,讓 Framer Motion 確保這個元素已經執行了退出動畫才從 DOM 中移除 Modal。
1import { motion, AnimatePresence } from 'framer-motion'; 2 3function App() { 4 return ( 5 <AnimatePresence> 6 <motion.dialog 7 initial={{ opacity: 0, y: 30 }} 8 animate={{ opacity: 1, y: 0 }} 9 exit={{ opacity: 0, y: 30 }} 10 open 11 /> 12 </AnimatePresence> 13 ); 14} 15
最後,再稍微講一下 variants
的用法,它可以為一個動畫效果建立一個變數,命名後可以讓你更方便複用這個效果。
舉例來說,剛剛的 Modal 就可以改為以下寫法,讓 Modal 的動畫效果看起來更好理解與管理。
1import { motion, AnimatePresence } from 'framer-motion'; 2 3function App() { 4 return ( 5 <AnimatePresence> 6 <motion.dialog 7 variants={{ 8 hidden: { opacity: 0, y: 30 }, 9 visible: { opacity: 1, y: 0 }, 10 }} 11 initial="hidden" 12 animate="visible" 13 exit="hidden" 14 open 15 /> 16 </AnimatePresence> 17 ); 18} 19