Sean's Blog

An image showing avatar

Hi, I'm Sean

這裡記錄我學習網站開發的筆記
歡迎交流 (ゝ∀・)b

LinkedInGitHub

為你的 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:動畫執行的細節設定,可以設置 durationtypestiffness 等等
  • AnimatePresence:延緩內部元件從 React Tree 中消失,等到動畫完成才真正地移除 DOM
  • whileHover: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 完成卡片滑出效果

這個範例我們透過 initialanimate 這兩個屬性,讓 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