什么是 GSAP
GSAP(GreenSock Animation Platform)是一个高性能的 JavaScript 动画库,用来让 DOM 元素、SVG、Canvas 甚至 Three.js 对象动起来。它的优势在于:
- 语法极简:一行代码就能描述一个完整动画。
- 性能强 :内部用
requestAnimationFrame,且自动做性能优化。 - 兼容好:支持几乎所有现代浏览器,甚至老 IE。
- 控制力强:可暂停、倒放、调速、监听回调(后续章节展开)。
一、基本使用
如何引入 GSAP
最简单的方式是通过 CDN 引入一个脚本文件。引入后,全局变量 gsap 即可使用:
javascript
<!-- 通过 CDN 引入(需要联网) -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
<!-- 之后即可在任意脚本中使用 gsap -->
<script>
gsap.to(".box", { x: 200, duration: 1 });
</script>
① gsap.to() --- 动画"到"目标状态
gsap.to(target, vars) 表示从元素当前状态 动画到 vars 里描述的目标状态。这是用得最多的方法。
javascript
<div class="box" id="toBox"></div>
<script>
function runTo() {
gsap.to("#toBox", {
x: 300, // 向右平移 300px
rotation: 360, // 旋转一圈
duration: 1.2, // 持续 1.2 秒
ease: "power2.out"
});
}
</script>

② gsap.from() --- 从指定状态"回到"当前
gsap.from(target, vars) 的反向写法:让元素从 vars 描述的状态,动画到它原本的样子。非常适合做「入场动画」------比如元素从上方淡入落到原位。
javascript
<div class="box blue" id="fromBox"></div>
<script>
function runFrom() {
gsap.from("#fromBox", {
y: -80, // 从上方 80px 处开始
opacity: 0, // 从透明开始
duration: 1,
ease: "back.out(1.7)"
});
}
</script>

③ gsap.fromTo() --- 明确指定起点与终点
gsap.fromTo(target, fromVars, toVars) 同时接收起点 和终点两个对象,最精确、最可控。当你必须保证动画从某个确定值开始时用它。
javascript
<div class="box pink" id="fromToBox"></div>
<script>
function runFromTo() {
gsap.fromTo("#fromToBox",
{ x: 0, scale: 0.3, opacity: 0 }, // 起点
{ x: 300, scale: 1, opacity: 1, // 终点
duration: 1.2, ease: "power3.out" }
); }
</script>

目标(target)可以怎么写
第一个参数 target 非常灵活,支持多种形式:
| 写法 | 说明 |
|---|---|
"#id" / ".class" |
CSS 选择器字符串 |
element |
直接传 DOM 元素,如 document.querySelector(".box") |
[el1, el2] |
元素数组,可同时动画多个 |
".box, #title" |
逗号分隔的复合选择器 |
javascript
<div class="row">
<div class="box"
id="m1"></div>
<div class="box blue"
id="m2"></div>
<div class="box pink"
id="m3"></div>
</div>
<script>
function runMulti() { gsap.to(["#m1", "#m2", "#m3"], { y: 50, rotation: 180, duration: 0.9, stagger: 0.15 }); }
</script>

最常用的动画属性
GSAP 几乎能补间任何数值型 CSS 属性。下面是最常用的一批(无需写单位,GSAP 会自动处理):
| 属性 | 作用 |
|---|---|
x / y |
平移(像素),等价于 transform: translate |
rotation |
旋转(角度)。rotationX/Y 为 3D 旋转 |
scale |
缩放。scaleX / scaleY 分开控制 |
opacity |
透明度 0~1 |
duration |
动画时长(秒),必会属性 |
delay |
延迟多少秒后开始 |
ease |
缓动曲线,决定快慢节奏(第 2 章重点) |
repeat |
重复次数,-1 为无限循环 |
yoyo |
配合 repeat,来回往返播放 |
往返循环
javascript
<div class="box" id="propBox"></div>
<script>
function runProp() {
gsap.to("#propBox", {
x: 300,
duration: 1,
ease: "sine.inOut",
repeat: -1, // 无限重复
yoyo: true // 来回往返
}); }
</script>

二、缓动与交错
同样的位移,不同的「速度节奏」会带来完全不同的观感。
什么是缓动(ease)
如果不设置 ease,GSAP 默认用 power1.out,动画会先快后慢。缓动描述的是属性值随时间变化的曲线:是匀速、先慢后快、还是带弹性回弹?
常用缓动一览
| 缓动 | 观感 |
|---|---|
none |
匀速直线运动,最生硬 |
power1/2/3/4.out |
逐渐减速,最常用、自然 |
sine.inOut |
柔和的正弦曲线 |
expo.out / circ.out |
极快起步、长尾缓停 |
back.out(1.7) |
冲过头再回弹,有弹性 |
elastic.out(1,0.3) |
弹簧般来回振荡 |
bounce.out |
像球落地一样弹跳 |
javascript
<div class="box" id="easeBox"></div>
<script>
function runEase() {
const ease = document.querySelector("#easeSel").value;
gsap.fromTo("#easeBox", { x: 0 }, { x: 320, duration: 1.2, ease });
}
</script>
sine.inOut

back.out(1.7)

elastic.out(1,0.3)

bounce.out

交错动画 stagger
当目标是一组元素时,stagger 让它们依次错开开始,而不是同时动。传一个数字表示每个元素之间的间隔秒数。
javascript
<div class="row" id="staggerRow">
<div class="box"></div><div class="box"></div><div class="box"></div>
<div class="box"></div><div class="box"></div><div class="box"></div>
</div>
<script>
function runStagger() {
gsap.from("#staggerRow .box", {
y: -40, opacity: 0, duration: 0.6,
stagger: 0.12 // 每个元素间隔 0.12 秒
});
}
</script>

stagger 的进阶写法
stagger 还可以是对象,控制方向、总量、起止位置:
| 参数 | 作用 |
|---|---|
each |
每个间隔秒数(等价数字写法) |
amount |
总时长,由 GSAP 自动均分间隔 |
from |
起始位置:"start"/"center"/"end"/"edges" |
grid |
二维网格 [行,列],用于网格状交错 |
javascript
<div class="row" id="staggerGrid" style="display:grid;grid-template-columns:repeat(5,1fr);gap:10px">
<div class="box blue"></div><div class="box blue"></div><div class="box blue"></div><div class="box blue"></div><div class="box blue"></div>
<div class="box blue"></div><div class="box blue"></div><div class="box blue"></div><div class="box blue"></div><div class="box blue"></div>
</div>
<script>
function runGrid() { gsap.from("#staggerGrid .box", { scale: 0, opacity: 0, duration: 0.5, stagger: { each: 0.08, from: "center", grid: [2, 5] } }); }
</script>

用 from: "center" 让中心元素最先动,向四周扩散------很适合做卡片/按钮组的入场。
ease决定动画快慢节奏,默认power1.outout系列最常用最自然;back/elastic/bounce带弹性趣味stagger让一组元素错开播放,数字即间隔秒数- 对象写法可控制
from/grid/amount,实现网格与方向化交错
三、时间线 Timeline
当动画一多,用一堆独立的 gsap.to() 很难控制先后顺序。时间线 gsap.timeline() 像一个「导演」,把多个动画按顺序/重叠地编排在一起,还能整体播放、暂停、倒放。
基本用法
用 gsap.timeline() 创建时间线,然后像 gsap 一样链式调用 .to() / .from() / .fromTo()。每个动画默认「接在上一个之后」。
javascript
<div class="box" id="tlBox"></div>
<script>
function runTL() {
if (tl) tl.kill();
tl = gsap.timeline();
tl.to("#tlBox", { x: 240, duration: 0.8 }) // 1) 向右
.to("#tlBox", { y: -40, duration: 0.6 }) // 2) 接在上一个之后向上
.to("#tlBox", { rotation: 360, duration: 0.8 }); // 3) 再旋转
}
</script>

用 defaults 统一默认参数
创建时间线时可传入 defaults,里面的属性会被所有子动画继承,减少重复书写。
javascript
const tl = gsap.timeline({ defaults: { duration: 0.6, ease: "power2.out" } });
tl.to("#a", { x: 100 }) // 自动用上面的 duration / ease
.to("#b", { y: 80 });
位置参数:精确控制时间点
每个子动画还可接收第三个参数------位置参数,用来指定它在时间线上的位置,打破「默认接在后面」的规则:
| 写法 | 含义 |
|---|---|
""(省略) |
接在上一个动画结束之后(默认) |
"<" |
与上一个动画同时开始 |
"<0.3" |
在上一个开始后 0.3 秒 |
"-=0.4" |
比默认位置提前 0.4 秒(重叠) |
"+=0.5" |
比默认位置延后 0.5 秒(留空隙) |
"myLabel" |
跳到名为 myLabel 的标签处 |
重叠播放示例
用 "-=0.4" 让第二个动画与第一个重叠 0.4 秒,过渡更连贯:
javascript
<div class="box blue" id="posBox">
<script>
function runPos() {
if (tlPos) tlPos.kill();
tlPos = gsap.timeline();
tlPos.to("#posBox", { x: 240, duration: 0.8 })
.to("#posBox", { rotation: 360, duration: 0.8 }, "-=0.4");
}
</script>

标签(labels):给时间点起名
用 .addLabel("名字") 在时间线上打标记,之后动画可定位到该标签,方便做「分支/回跳」。
javascript
const tl = gsap.timeline();
tl.to(box, { x: 200 })
.addLabel("turn") // 在此处打标签
.to(box, { rotation: 180 }, "turn") // 跳到 turn 处开始
.to(box, { y: -60 }, "turn+=0.2");
时间线也能整体控制
时间线本身也是一个「动画对象」,可以设 repeat、yoyo
javascript
<div class="box pink" id="loopBox"></div>
<script>
function runLoop() {
if (tlLoop) tlLoop.kill();
tlLoop = gsap.timeline({ repeat: -1, yoyo: true, repeatDelay: 0.3 });
tlLoop.to("#loopBox", { x: 240, duration: 0.7 })
.to("#loopBox", { rotation: 360, duration: 0.7 });
}
</script>

四、控制与进阶
动画不只是「播放一次」
控制动画的播放状态
GSAP 的动画(包括前章的时间线)都会返回一个实例对象,调用它的方法即可操控:
| 方法 | 作用 |
|---|---|
.play() |
从当前位置继续播放 |
.pause() |
暂停 |
.reverse() |
倒放(从当前位置往回) |
.restart() |
重头播放 |
.seek(t) |
跳到时间 t(秒)处 |
.progress(p) |
跳到进度 p(0~1) |
.timeScale(s) |
整体速度倍率,2 加速、0.5 慢放 |
.kill() |
销毁动画、清除内联样式 |
javascript
<div class="box" id="ctrlBox"></div>
<script>
const tween = gsap.to("#ctrlBox", { x: 300, rotation: 360, duration: 2, ease: "none", paused: true });
ctrlPlay.onclick = () => tween.play();
ctrlPause.onclick = () => tween.pause();
ctrlReverse.onclick = () => tween.reverse();
ctrlRestart.onclick = () => tween.restart();
</script>

监听生命周期回调
在 vars 里传入回调,可在动画的关键时刻执行代码:
| 回调 | 触发时机 |
|---|---|
onStart |
动画开始第一次播放 |
onComplete |
动画完整结束 |
onUpdate |
每一帧更新(适合同步 UI) |
onRepeat |
每次重复时 |
javascript
<div class="box blue" id="cbBox"></div>
<script>
function runCb() {
const log = (m) => (document.querySelector("#cbLog").textContent = m);
if (cbTween) cbTween.kill();
gsap.set("#cbBox", { x: 0 });
cbTween = gsap.to("#cbBox", {
x: 300, duration: 1.2,
onStart: () => log("▶ 动画开始"),
onUpdate: function () { log("帧: " + Math.round(this.progress() * 100) + "%"); },
onComplete: () => log("✔ 完成")
});
}
</script>

实用案例 ①:加载进度条
javascript
<div class="demo">
<div style="background:var(--code-bg);border:1px solid var(--border);border-radius:10px;height:22px;overflow:hidden;margin-bottom:10px">
<div id="bar" style="height:100%;width:0%;background:linear-gradient(90deg,var(--accent),#5fae00)"></div>
</div>
<div class="row" style="align-items:center">
<button class="btn primary" onclick="runBar()">开始加载 ▶</button>
<span id="barText" style="color:var(--text-dim);font-size:13px">0%</span>
</div>
</div>
<script>
function runBar() {
if (barTween) barTween.kill();
gsap.set("#bar", { width: "0%" });
document.querySelector("#barText").textContent = "0%";
barTween = gsap.to("#bar", {
width: "100%", duration: 2, ease: "power1.inOut",
onUpdate: function () {
const p = Math.round(this.progress() * 100);
document.querySelector("#barText").textContent = p + "%";
}
});
}
</script>

实用案例 ②:按钮悬停反馈
用 mouseenter / mouseleave 触发动画,配合 timeScale 让悬停时快速放大、离开时缓慢回弹。
javascript
<button class="btn primary" id="hoverBtn" style="font-size:16px;padding:14px 28px">悬停我</button>
const btn = document.querySelector("#hoverBtn");
const hover = gsap.to(btn, { scale: 1.12, duration: 0.3, paused: true, ease: "power2.out" });
btn.onmouseenter = () => hover.play();
btn.onmouseleave = () => hover.reverse();

五、进阶插件 官方扩展
GSAP 核心之外,官方提供了一批插件来应对特定场景。
① ScrollTrigger --- 滚动到位置才触发
ScrollTrigger 让动画与页面滚动绑定:元素进入视口才播放,离开可反向。最典型的用途就是「滚动揭示(scroll reveal)」------内容随滚动逐块浮现。
javascript
<div class="reveal-wrap">
<div class="reveal-card"><b>卡片 A</b> --- 进入视口时从下方淡入。</div>
<div class="reveal-card"><b>卡片 B</b> --- 同样的 ScrollTrigger 配置。</div>
<div class="reveal-card"><b>卡片 C</b> --- 滚动揭示非常适合长图文。</div>
<div class="reveal-card"><b>卡片 D</b> --- 可配合 toggleActions 控制往返。</div>
<div class="reveal-card"><b>卡片 E</b> --- 也可做进度条式滚动联动。</div>
</div>
<script>
gsap.registerPlugin(ScrollTrigger);
gsap.utils.toArray(".reveal-card").forEach((card) => {
gsap.from(card, {
y: 60, opacity: 0, duration: 0.8, ease: "power2.out",
scrollTrigger: { trigger: card, start: "top 85%" }
});
});
</script>

② MotionPathPlugin --- 沿任意路径运动
让元素沿着一条 SVG 路径(曲线、圆形、任意形状)运动。只需把路径元素传给 motionPath.path,并用 align 让元素贴合路径。
javascript
<svg class="motion-svg" viewBox="0 0 400 150" preserveAspectRatio="none">
<path id="track" d="M20,120 C70,10 150,10 200,75 S330,140 380,30" />
<circle id="dot" r="9" />
</svg>
<script>
gsap.registerPlugin(ScrollTrigger);
gsap.utils.toArray(".reveal-card").forEach((card) => {
gsap.from(card, {
y: 60, opacity: 0, duration: 0.8, ease: "power2.out",
scrollTrigger: { trigger: card, start: "top 85%" } // 顶部到达视口 85% 处触发
});
});
</script>

③ Flip --- 布局变化自动补间(FLIP 技术)
当你用 CSS/JS 改变了元素的位置或大小(如重新排序、切换网格/列表),Flip 会记录「前后状态」并自动生成平滑过渡,无需手写坐标。
javascript
<div class="flip-grid" id="flipGrid">
<div class="flip-item">1</div><div class="flip-item">2</div><div class="flip-item">3</div>
<div class="flip-item">4</div><div class="flip-item">5</div><div class="flip-item">6</div>
<div class="flip-item">7</div><div class="flip-item">8</div>
</div>
<script>
gsap.registerPlugin(Flip);
function shuffleFlip() {
const state = Flip.getState(".flip-item"); // 1) 记录当前布局
// 2) 改变 DOM 顺序(真实重排)
const grid = document.querySelector("#flipGrid");
[...grid.children].sort(() => Math.random() - 0.5)
.forEach(el => grid.appendChild(el));
// 3) 让 Flip 自动补间到新布局
Flip.from(state, { duration: 0.6, ease: "power2.out", stagger: 0.04 });
}
</script>

④ Draggable --- 让元素可拖拽并联动
Draggable 让元素支持鼠标/触摸拖拽,可限制方向或边界,并在拖动过程中回调,方便与其他动画联动。
javascript
<div class="drag-stage" id="dragStage">
<div class="drag-box" id="dragBox">拖我</div>
</div>
<script>
gsap.registerPlugin(Draggable);
Draggable.create("#dragBox", {
type: "x,y", // 允许 xy 自由拖动
bounds: "#dragStage", // 限制在容器内
onDrag: function() { // 拖动时实时联动
dragInfo.textContent = `x:${Math.round(this.x)} y:${Math.round(this.y)}`;
}
});
</script>

⑤ TextPlugin --- 逐字「打字机」效果
TextPlugin 让元素的文字内容参与补间,从而做出逐字出现/替换的打字机效果。
javascript
<div class="type-text" id="typeText"></div>
<script>
gsap.registerPlugin(TextPlugin);
gsap.to("#typeText", {
text: "你好,我是用 GSAP TextPlugin 逐字打出来的文字。",
duration: 2.2,
ease: "none" // 匀速逐字出现
});
</script>
