返回目录

从补间到叙事:GSAP 动画实战 · 公开阅读

第1章 GSAP 概述

了解 GSAP 的用途、安装方式,并创建第一个动画。

本章小节

1.1 GSAP 是什么

有些网站打开的瞬间,元素像被施了魔法一样优雅地滑入、旋转、淡入——那些丝滑的产品页面和令人惊艳的着陆页动画,背后大概率是 GSAP 在干活。

GSAP(GreenSock Animation Platform)是一套 JavaScript 动画工具。核心计算属性的中间值,CSSPlugin 等插件将值写入 DOM;Canvas 和 WebGL 场景由你的绘制代码呈现。动画能否流畅,需要结合页面和设备测量。

1.2 发展简史

GSAP 源于 GreenSock 在 ActionScript/Flash 时代的补间工具,后来发展为 JavaScript 动画库。理解今天的 API,重点是区分旧版本语法与 GSAP 3。

  • GSAP 3.0 在 2019 年发布,整合了 TweenLite/TweenMax 等入口。
  • ScrollTrigger、Flip、Observer 等插件补充滚动、布局和输入处理。
  • GSAP 3.13 将此前的会员插件纳入公开安装包。参见 3.13 发布说明。

本书使用 gsap.to()、gsap.from() 和 gsap.timeline()。阅读旧教程时,先核对版本、插件名称和安装方式。

1.3 为什么选择 GSAP

1.3.1 先区分计算与渲染

GSAP 使用帧循环,根据时间和缓动计算属性值。DOM 是否需要布局、绘制或合成,仍由浏览器决定;使用 GSAP 不会绕过浏览器渲染成本。

1.3.2 按需求选择工具

需求可考虑的实现判断依据
简单悬停、颜色过渡CSS transition状态少,无需脚本编排
原生可控制动画Web Animations API浏览器支持与控制需求
多段串行、并行、逆放GSAP Timeline可组合的播放头和位置参数
滚动联动、布局过渡、SVG 变形GSAP 对应插件实际功能与项目体积
Canvas / Three.js 属性动画GSAP + 绘制器GSAP 更新值,绘制器负责显示

没有统一的帧率排名。比较时要使用相同 DOM、属性、目标设备和测量条件,记录布局、绘制和主线程耗时。

1.3.3 使用边界

GSAP 不要求 React 或 Vue,但会写入元素的内联样式;SplitText 会临时修改文本 DOM。卸载时用 context.revert() 恢复已记录的样式,拆分文本等资源也要还原。仅 kill() 会停止动画,不等于把页面恢复到动画之前。

1.4 GSAP 3.x 架构概览

GSAP 3.x 采用核心 + 插件模式:核心自带基础功能,高级功能通过插件扩展。

gsap (核心)
├── gsap.to() / gsap.from() / gsap.fromTo()     ← 三大核心 API
├── gsap.timeline()                               ← 时间线
├── gsap.registerPlugin()                         ← 插件注册
├── gsap.set()                                    ← 零时间设置
├── gsap.utils                                     ← 工具方法
├── gsap.eases                                     ← 缓动函数库
└── 插件
    ├── ScrollTrigger   —— 滚动驱动动画
    ├── Flip            —— 布局变化动画
    ├── ScrollTo        —— 平滑滚动
    ├── Draggable       —— 拖拽交互
    ├── MorphSVG        —— SVG 形变
    ├── DrawSVG         —— SVG 描边动画
    └── MotionPath      —— 路径跟随动画

GSAP 3.13 起,核心与全部插件都免费,包括商业使用。MorphSVG、DrawSVG、SplitText 等均包含在公开的 gsap npm 包中,无需 Club GSAP 会员或私有仓库令牌。详见 官方发布说明。

1.5 安装与引入

下面三种方式,按你的场景选择:

方式一:CDN(最快上手)

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/gsap.min.js"></script>

方式二:npm(工程化项目)

npm install gsap
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';

gsap.registerPlugin(ScrollTrigger);

方式三:ES Module CDN

<script type="module">
  import gsap from 'https://cdn.skypack.dev/gsap@3.13.0';
</script>

现代项目用 npm,快速试验用 CDN。刚入门的话,CDN 一行代码搞定。

1.6 第一个 GSAP 动画

来看第一个例子。5 行代码,让一个方块飞起来:

<!DOCTYPE html>
<html>
<head><style>
  .box {
    width: 80px; height: 80px;
    background: #6c5ce7;
    border-radius: 8px;
  }
</style></head>
<body>
<div class="box"></div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/gsap.min.js"></script>
<script>
  gsap.to('.box', {
    x: 300,          // 向右移动 300px
    duration: 1.5,   // 持续 1.5 秒
    rotation: 360,   // 旋转一圈
    scale: 1.5,      // 放大到 1.5 倍
    ease: 'power2.out'
  });
</script>
</body>
</html>

拆解这 5 行:

  • gsap.to() —— 最常用的动画方法,从当前状态过渡到目标状态
  • .box —— 选择器(支持 CSS 选择器、DOM 元素、数组)
  • x: 300 —— 对 transform: translateX(300px) 做动画
  • rotation: 360 —— 对 transform: rotate(360deg) 做动画
  • ease: 'power2.out' —— 缓出效果,开始快、结束慢

GSAP 用 x / y / rotation 这些简写属性而不是直接写 transform。它自动处理 transform 的内部计算,避免了 CSS transform 各组件互相覆盖的问题:你只管说要动什么,GSAP 搞定底层细节。

再看看 CSS Animation 怎么做同样的事

/* CSS 版本——繁琐且不可控 */
.box {
  animation: fly 1.5s ease-out forwards;
}
@keyframes fly {
  100% {
    transform: translateX(300px) rotate(360deg) scale(1.5);
  }
}

对比:

维度GSAPCSS Animation
代码量5 行 JS6+ 行 CSS + 定义关键帧
暂停/恢复.pause() / .resume() 一行搞定需要操作 animation-play-state
中途修改直接调用 API需要重新定义 animation
精确回调onComplete / onUpdate 等animationend 事件,精度有限
串联编排Timeline 一行搞定需要手动计算 animation-delay

这就是为什么大量开发者从 CSS 动画转向 GSAP。

把这一节跑一遍:位移与旋转

先确认坐标:x 是相对当前布局位置的变换位移,rotation 使用度。一个 Tween 可以同时更新多个属性,但最终布局位置不因此重新计算。演示中的终点根据容器宽度测量,避免移动端越界。

实验:位移与旋转

GSAP 3.13 / 实验

亲手调整参数,观察动画差异。点击开启后再播放,不会自动启动动画。

观察与验证:暂停在 50%,分别比较 none 与 power2.out。旋转圈数相同,但半程角度不同,原因是同一个缓动同时影响位移与旋转。

1.7 GSAP 不仅是 UI 动画

GSAP 不只是"更好用的 CSS 动画库"。它能驱动任何 JavaScript 对象,哪怕跟 DOM 毫无关系:

// 动画驱动任意对象
const gameState = { health: 0 };

gsap.to(gameState, {
  health: 100,
  duration: 2,
  onUpdate: () => {
    document.getElementById('health-bar').style.width = gameState.health + '%';
  }
});

// 滚动数字效果
const counter = { value: 0 };
gsap.to(counter, {
  value: 9999,
  duration: 3,
  onUpdate: () => {
    document.getElementById('count').textContent = Math.round(counter.value);
  }
});

GSAP 不关心你要动画什么——DOM、Canvas、Three.js 场景、纯数据——它只负责在起止值之间平滑插值。 你给它起点和终点,中间的过程它全包了。

把这一节跑一遍:普通对象补间

目标不一定是 DOM。这里先创建普通对象的 number 字段,Tween 更新原始浮点数,再由 onUpdate 把它格式化为可见数字。显示四舍五入不会改变 Tween 内部值。

实验:普通对象补间

GSAP 3.13 / 实验

亲手调整参数,观察动画差异。点击开启后再播放,不会自动启动动画。

观察与验证:把目标改为 2400,定位到 50%。先计算预期原始值,再核对读数。Canvas/WebGL 也是先更新对象,再调用自己的绘制代码。

1.8 GSAP vs 主流框架的协作

GSAP 与框架无关,和所有主流框架都能无缝配合:

// React 中使用
useEffect(() => {
  const box = boxRef.current;
  gsap.to(box, { x: 200, duration: 1 });

  return () => { gsap.killTweensOf(box); }; // cleanup
}, []);

// Vue 中使用
onMounted(() => {
  gsap.to('.vue-box', { y: -50, duration: 0.8 });
});

// 原生 JS
document.addEventListener('DOMContentLoaded', () => {
  gsap.to('#native-box', { scale: 1.2, duration: 0.5 });
});

1.9 常见陷阱

陷阱 1:版本混淆

新手最容易踩的坑——你跟着网上的教程抄代码,结果发现跑不起来,因为教程可能是 2.x 时代的。

// ❌ 2.x 旧写法——GSAP 3 中会报错
TweenMax.to('.box', 1, { x: 100 });

// ✅ GSAP 3.x 正确写法
gsap.to('.box', { x: 100, duration: 1 });

GSAP 3 统一了 API:gsap.to() / gsap.from() / gsap.fromTo() / gsap.timeline()。不再有 TweenMax、TweenLite、TimelineMax 的区分。旧教程和 StackOverflow 答案中大量使用旧语法,看到 TweenMax 开头的就要警惕。

陷阱 2:CDN 链接过期

<!-- ❌ 版本号未知或过旧 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/2.1.3/TweenMax.min.js"></script>

<!-- ✅ 锁定主版本的最新稳定版 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.13.0/gsap.min.js"></script>

始终从 cdnjs 或官方文档确认最新版本号。别直接抄别人教程里的链接,那可能已经是好几年前的版本了。

陷阱 3:忘记注册插件

import ScrollTrigger from 'gsap/ScrollTrigger';

// ❌ 忘记注册,ScrollTrigger 不生效
ScrollTrigger.create({ trigger: '.section', start: 'top 80%' });

// ✅ 先注册
gsap.registerPlugin(ScrollTrigger);
ScrollTrigger.create({ trigger: '.section', start: 'top 80%' });

就像新电器插上插座但忘了按开关——看起来一切正常,就是不工作。

陷阱 4:误以为 GSAP 是 CSS 动画库

GSAP 的本质是属性插值引擎,CSS 属性只是它能力的一个子集。不要用 CSS 动画的思维来限制它。