GSAP 動畫入門
GSAP 與 Motion 的差異比較,以及 GSAP 核心 API、Timeline、ScrollTrigger 實務用法
Overview##
GSAP(GreenSock Animation Platform) 是一個專業級的 JavaScript 動畫函式庫。它不綁定任何框架,可以動畫化任何 JavaScript 可操作的數值——CSS 屬性、SVG、Canvas、甚至自訂物件。
選擇動畫工具之前,先理解兩大主流的根本差異。
GSAP vs Motion##
Motion(前身 Framer Motion)和 GSAP 代表兩種不同的動畫範式:
範式差異###
// Motion(宣告式):描述「要什麼狀態」
<motion.div animate={{ x: 100, opacity: 1 }} />;
// GSAP(命令式):描述「怎麼做動畫」
gsap.to('.box', { x: 100, opacity: 1, duration: 1 });
Motion 把動畫當作元件的 props,跟 React 的宣告式思維一致。GSAP 則是直接操作 DOM,告訴瀏覽器「把這個元素在 1 秒內移到 x: 100」。
比較表###
| 面向 | GSAP | Motion |
|---|---|---|
| 範式 | 命令式 | 宣告式 |
| 框架依賴 | 無(原生 JS) | React / Vue |
| 學習曲線 | 中等 | 低(React 開發者) |
| DOM 操作 | 直接操作(ref) | 自動管理 |
| Timeline | 強大,精細控制 | 陣列語法,較簡潔 |
| ScrollTrigger | 內建插件,功能豐富 | whileInView props |
| Layout 動畫 | 需手動計算 | layout prop 自動處理 |
| Exit 動畫 | 需手動管理 | AnimatePresence 自動處理 |
| 檔案大小 | ~28KB(核心) | ~18KB |
| 授權 | 商用免費(部分插件付費) | MIT |
Timeline 寫法對比###
// GSAP:鏈式呼叫,精細控制時間點
const tl = gsap.timeline();
tl.to('h1', { opacity: 1, duration: 0.5 })
.to('p', { y: 0, duration: 0.3 }, '-=0.2') // 提前 0.2s 開始
.to('.btn', { scale: 1, duration: 0.4 });
// Motion:陣列語法,用 at 控制時間
animate([
['h1', { opacity: 1 }, { duration: 0.5 }],
['p', { y: 0 }, { duration: 0.3, at: '-0.2' }],
['.btn', { scale: 1 }, { duration: 0.4 }],
]);
React 整合對比###
// Motion:宣告式,跟 React 思維一致
function Card() {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
whileHover={{ scale: 1.05 }}
/>
);
}
// GSAP:命令式,需要 ref + useGSAP hook
function Card() {
const container = useRef();
useGSAP(
() => {
gsap.from('.card', { opacity: 0, y: 20, duration: 0.5 });
},
{ scope: container },
);
return (
<div ref={container}>
<div className="card" />
</div>
);
}
選擇指南###
| 場景 | 推薦 |
|---|---|
| React UI 互動(hover、進場、離場) | Motion |
| 複雜序列動畫、精細時間軸控制 | GSAP |
| 捲動驅動動畫(ScrollTrigger) | GSAP |
| 跨框架或原生 JS 專案 | GSAP |
| Layout 動畫(元素位置切換) | Motion |
| 需要商用免費的完整方案 | 依需求評估授權 |
Tip
兩者不互斥。在同一個專案中,可以用 Motion 處理 UI 互動動畫,用 GSAP 處理複雜的捲動和序列動畫。
Core API##
GSAP 的核心只有三個方法:to、from、fromTo。
gsap.to()###
從目前狀態動畫到目標狀態——最常用的方法:
// 在 1 秒內,把 .box 移到 x: 200 並淡出
gsap.to('.box', {
x: 200,
opacity: 0,
duration: 1,
});
gsap.from()###
從指定狀態動畫到目前狀態——適合進場動畫:
// .box 從 opacity: 0、y: -50 的位置動畫到原始位置
gsap.from('.box', {
opacity: 0,
y: -50,
duration: 0.8,
});
gsap.fromTo()###
同時指定起點和終點——完全控制:
gsap.fromTo(
'.box',
{ opacity: 0, x: -100 }, // from
{ opacity: 1, x: 0, duration: 1 }, // to
);
常用屬性###
| 屬性 | 說明 | 範例 |
|---|---|---|
x / y | 位移(transform) | x: 100(等同 translateX) |
rotation | 旋轉(度) | rotation: 360 |
scale | 縮放 | scale: 1.5 |
opacity | 透明度 | opacity: 0 |
duration | 動畫時長(秒) | duration: 1 |
delay | 延遲開始(秒) | delay: 0.5 |
ease | 緩動函數 | ease: "power2.out" |
stagger | 多元素依序動畫的間隔 | stagger: 0.1 |
Ease###
緩動函數決定動畫的速度曲線:
// 常用 ease
gsap.to('.box', { x: 200, ease: 'power1.out' }); // 輕微減速
gsap.to('.box', { x: 200, ease: 'power2.inOut' }); // 先加速後減速
gsap.to('.box', { x: 200, ease: 'bounce.out' }); // 彈跳效果
gsap.to('.box', { x: 200, ease: 'elastic.out' }); // 彈性效果
gsap.to('.box', { x: 200, ease: 'back.out(1.7)' }); // 超出後回彈
| Ease | 效果 |
|---|---|
"none" | 線性(等速) |
"power1" ~ "power4" | 漸進式加/減速,數字越大越明顯 |
"bounce" | 彈跳 |
"elastic" | 彈性 |
"back" | 超出後回彈 |
.in / .out / .inOut | 加速 / 減速 / 先加後減 |
Tip
GSAP 提供 Ease Visualizer 讓你視覺化每種緩動函數的效果。選擇 ease 時建議先在 visualizer 上測試。
Stagger###
讓多個元素依序動畫,而不是同時開始:
// 5 個 .card 元素,每個間隔 0.15 秒依序淡入
gsap.from('.card', {
opacity: 0,
y: 30,
duration: 0.5,
stagger: 0.15,
});
// 進階:從中間向兩側展開
gsap.from('.card', {
opacity: 0,
scale: 0,
stagger: {
each: 0.1,
from: 'center',
},
});
Timeline##
單個 gsap.to() 只能做一段動畫。Timeline 讓你編排多段動畫的順序和時間:
const tl = gsap.timeline();
// 預設:依序播放
tl.to('.title', { opacity: 1, duration: 0.5 })
.to('.subtitle', { opacity: 1, y: 0, duration: 0.3 })
.to('.content', { opacity: 1, duration: 0.4 });
Position Parameter###
控制動畫在時間軸上的插入位置:
const tl = gsap.timeline();
tl.to('h1', { x: 100, duration: 1 })
// 絕對時間:在 0.5 秒開始
.to('h2', { x: 100, duration: 1 }, 0.5)
// 相對時間:在上一個動畫結束前 0.2 秒開始
.to('h3', { x: 100, duration: 1 }, '-=0.2')
// 相對時間:在上一個動畫結束後 0.5 秒開始
.to('h4', { x: 100, duration: 1 }, '+=0.5')
// 同時開始:與上一個動畫同步
.to('h5', { x: 100, duration: 1 }, '<');
| Position | 說明 |
|---|---|
0.5 | 絕對時間 0.5 秒 |
"-=0.2" | 前一個動畫結束前 0.2 秒 |
"+=0.5" | 前一個動畫結束後 0.5 秒 |
"<" | 與前一個動畫同時開始 |
">" | 前一個動畫結束時 |
Timeline 控制###
const tl = gsap.timeline({ paused: true });
tl.to('.box', { x: 200 }).to('.box', { y: 100 });
tl.play(); // 播放
tl.pause(); // 暫停
tl.reverse(); // 反轉播放
tl.restart(); // 重新開始
tl.progress(0.5); // 跳到 50%
Note
Timeline 可以巢狀——一個 Timeline 可以作為另一個 Timeline 的一段動畫,適合組織複雜的動畫場景。
ScrollTrigger##
ScrollTrigger 是 GSAP 最受歡迎的插件——讓動畫由捲動位置驅動。
基本用法###
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);
// 元素進入視窗時觸發動畫
gsap.from('.section', {
opacity: 0,
y: 50,
duration: 1,
scrollTrigger: {
trigger: '.section', // 觸發元素
start: 'top 80%', // 元素頂部到達視窗 80% 位置時開始
end: 'bottom 20%', // 元素底部到達視窗 20% 位置時結束
},
});
Scrub###
讓動畫進度跟捲動位置同步——捲多少,動畫就播多少:
gsap.to('.progress-bar', {
width: '100%',
scrollTrigger: {
trigger: '.article',
start: 'top top',
end: 'bottom bottom',
scrub: true, // 動畫跟著捲動走
},
});
// scrub: 1 → 加上 1 秒的平滑延遲
Pin###
在捲動期間「釘住」元素:
gsap.to('.hero-text', {
opacity: 0,
scrollTrigger: {
trigger: '.hero',
start: 'top top',
end: '+=500', // 釘住 500px 的捲動距離
pin: true, // 釘住 trigger 元素
scrub: true,
},
});
Timeline + ScrollTrigger###
const tl = gsap.timeline({
scrollTrigger: {
trigger: '.container',
pin: true,
start: 'top top',
end: '+=1000',
scrub: 1,
snap: {
snapTo: 'labels',
duration: { min: 0.2, max: 3 },
},
},
});
tl.addLabel('start')
.from('.step-1', { opacity: 0, y: 50 })
.addLabel('step1')
.from('.step-2', { opacity: 0, y: 50 })
.addLabel('step2')
.from('.step-3', { opacity: 0, y: 50 })
.addLabel('end');
Warning
ScrollTrigger 在 SPA 框架(React、Vue)中需要注意清理。元件卸載時必須呼叫 ScrollTrigger.kill() 或使用 useGSAP hook 自動管理生命週期。
Quiz##
Summary##
- GSAP = 命令式(精細控制),Motion = 宣告式(React 友善),兩者可共存
- 核心 API 只有三個:
to(目前→目標)、from(指定→目前)、fromTo(指定→指定) ease控制速度曲線,stagger讓多元素依序動畫- Timeline 編排動畫序列,Position Parameter 控制時間點(
-=0.2、<、+=0.5) - ScrollTrigger 讓動畫由捲動驅動——
scrub同步進度、pin釘住元素 - SPA 框架中使用 ScrollTrigger 需注意清理,避免記憶體洩漏
留言 (0)
登入後即可留言