GSAP(GreenSock Animation Platform)是一个强大的 JavaScript 动画库,而 ScrollTrigger 是其核心插件之一,用于创建基于滚动的动画效果(scroll-driven animations)。它允许你将动画进度与页面滚动位置绑定,实现如视差、固定元素、擦除(scrub)动画等高级交互,而无需依赖 CSS 原生滚动驱动(虽然 CSS 版本已成熟,但 GSAP 提供更灵活的控制和跨浏览器兼容)。ScrollTrigger 于 GSAP 3.x 中引入,目前(2025 年)版本已稳定在 3.12+,性能优化出色,尤其适合创意网站、scrollytelling 和交互式落地页。
相比纯 CSS 滚动驱动动画,GSAP + ScrollTrigger 的优势在于:
- 灵活性:支持复杂时间线(Timeline)、回调函数和无缝集成其他 GSAP 插件。
- 性能:利用 requestAnimationFrame(RAF)同步,避免主线程阻塞;内置防抖和节流。
- 兼容:处理触屏、横向滚动和自定义滚动容器。
- 易用:无需监听 scroll 事件,手动计算位置。
安装和基本设置
-
安装 GSAP:通过 npm/yarn(推荐 React/Vue 等框架)或 CDN。
- npm:
npm install gsap - CDN:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>和<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
- npm:
-
注册插件 :在代码开头添加
gsap.registerPlugin(ScrollTrigger);。这激活 ScrollTrigger,避免 tree-shaking 问题。 -
基本结构:ScrollTrigger 可以嵌入 GSAP tween/timeline 中,或独立创建。
- 嵌入式:
gsap.to(target, { ...animationProps, scrollTrigger: { ...config } }); - 独立式:
ScrollTrigger.create({ ...config });(用于非动画触发,如类切换或回调)。
- 嵌入式:
-
刷新机制 :在 DOM 加载后或窗口 resize 时调用
ScrollTrigger.refresh();,以重新计算位置(尤其动态内容)。
关键概念
ScrollTrigger 的核心是基于滚动位置(而非时间)驱动动画。以下是主要术语:
| 概念 | 描述 | 示例值/用法 |
|---|---|---|
| Trigger | 触发元素(selector 或元素),其位置决定动画开始/结束。默认相对视口计算。 | ".box" 或 #id |
| Start | 动画开始的滚动位置。字符串("top bottom" 表示 trigger 顶部到达视口底部)、像素值或函数。默认 "top bottom"(非 pin 时)。 | "top 80%"(trigger 顶部到达视口 80% 时开始) |
| End | 动画结束的滚动位置。类似 start,支持相对值如 "+=500"(从 start 后 500px)。默认 "bottom top"。 | "+=300" 或 "bottom center" |
| Scrub | 将动画进度与滚动直接绑定。true:即时擦除;数字(如 1):平滑追赶(秒)。 | scrub: true(滚动前进/后退动画同步) |
| Pin | 在动画活跃期固定元素。true:固定 trigger;字符串/元素:自定义目标。自动添加 pinSpacing(padding 防止内容塌陷)。 | pin: true(固定 section,直到 end) |
| Markers | 开发调试标记(绿色 start、红色 end)。布尔或对象自定义样式。 | markers: true 或 {startColor: "blue"} |
其他概念:
- Snap:滚动结束时自动吸附到指定进度(如标签或数组值),增强交互感。
- ToggleActions:控制动画行为,如 "play pause resume reset"(进入/离开/返回/离开后)。
- Callbacks :如
onEnter、onUpdate(进度更新时执行自定义逻辑)。
配置选项
ScrollTrigger 配置是一个对象,支持丰富选项。常见包括:
- animation:绑定的 tween 或 timeline。
- scroller:自定义滚动容器(默认 document),如 ".custom-scroller"。
- horizontal:true 时支持水平滚动。
- toggleClass :激活时添加/移除类,如
{className: "active", targets: ".nav"}。 - snap :吸附配置,如
{snapTo: [0, 0.5, 1], duration: 0.5}。 - invalidateOnRefresh:true 时刷新重置动画(默认 false,性能更好)。
- anticipatePin:true 预判快速滚动,避免 pin 延迟闪烁(3.8.0+)。
- clamp:3.12+ 新增,确保 start/end 在边界内。
完整配置示例:
js
scrollTrigger: {
trigger: ".section",
start: "top top",
end: "+=100%",
scrub: 1,
pin: true,
markers: true,
onUpdate: self => console.log(self.progress) // 0-1 进度
}
实现步骤
- 准备 HTML/CSS:确保页面有足够高度(e.g., height: 300vh)以滚动。目标元素有初始样式。
- 创建动画:用 gsap.to/from/timeline 定义。
- 添加 ScrollTrigger:嵌入配置。
- 测试与调试:启用 markers,检查控制台。
- 优化 :用
ScrollTrigger.batch()批量处理多个类似触发器(stagger 效果)。
常见示例
1. 简单淡入(Fade-In on Enter Viewport)
当元素进入视口时淡入。
js
gsap.from(".fade-element", {
opacity: 0,
y: 50,
scrollTrigger: {
trigger: ".fade-element",
start: "top 80%", // trigger 顶部到达视口 80% 时开始
toggleActions: "play none none reverse" // 进入播放,离开反转
}
});
2. 固定部分(Pinning Sections)
固定一个部分,直到滚动完指定距离。
js
gsap.timeline({
scrollTrigger: {
trigger: ".pin-section",
start: "top top",
end: "+=500", // 固定 500px 滚动距离
pin: true,
scrub: true
}
})
.to(".pin-content", { x: 300 }); // 在固定期内移动
3. 视差效果(Parallax)
背景层随滚动移动,创建深度感。
js
gsap.to(".parallax-bg", {
yPercent: -20, // 向下移动 20%
ease: "none",
scrollTrigger: {
trigger: ".parallax-container",
scrub: true // 直接绑定滚动
}
});
4. 水平滚动(Horizontal Scrolling)
垂直滚动驱动水平移动,常用于画廊。
js
const sections = gsap.utils.toArray(".horizontal-panel");
gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: "none",
scrollTrigger: {
trigger: ".horizontal-container",
pin: true,
scrub: 1,
snap: 1 / (sections.length - 1), // 吸附到每个面板
end: () => "+=" + document.querySelector(".horizontal-container").offsetWidth
}
});
5. 复杂时间线(Timeline with Labels and Snap)
多步动画,按滚动进度执行。
js
let tl = gsap.timeline({
scrollTrigger: {
trigger: ".container",
pin: true,
start: "top top",
end: "+=500",
scrub: 1,
snap: { snapTo: "labels", duration: { min: 0.2, max: 3 } } // 吸附到标签
}
});
tl.addLabel("start")
.from(".box p", { scale: 0.3, rotation: 45, opacity: 0 })
.addLabel("color")
.from(".box", { backgroundColor: "#28a92b" })
.addLabel("spin")
.to(".box", { rotation: 360 })
.addLabel("end");
与 Lenis 的集成(Smooth Scrolling)
Lenis 提供丝滑滚动,GSAP 可无缝集成以避免抖动:
-
安装 Lenis:
npm install lenis -
初始化 Lenis:
const lenis = new Lenis();并在 RAF 中更新lenis.raf(time * 1000); -
代理 ScrollTrigger:
jsScrollTrigger.scrollerProxy("body", { scrollTop(value) { return arguments.length ? lenis.scrollTo(value, { immediate: true }) : lenis.scroll; }, getBoundingClientRect() { return { top: 0, left: 0, width: window.innerWidth, height: window.innerHeight }; } }); lenis.on("scroll", ScrollTrigger.update); gsap.ticker.add((time) => lenis.raf(time * 1000)); gsap.ticker.lagSmoothing(0);
这确保 ScrollTrigger 与 Lenis 的虚拟滚动同步。避免使用 GSAP 的旧 ScrollSmoother(基于 transform),Lenis 更轻量且兼容原生滚动条。
最佳实践
- 性能:限制数据点(<40),用 ease: "none" 于 scrub。避免在 onUpdate 中重计算。批量创建触发器以 stagger。
- 移动端 :用
ScrollTrigger.isTouch区分触屏逻辑。启用fastScrollEnd快速滚动时完成动画。 - 响应式:用 GSAP.matchMedia()(非旧 matchMedia)创建不同断点的 ScrollTrigger。
- 调试:markers + 控制台日志。刷新优先级(refreshPriority)确保顺序。
- 避免问题:不 scroll-jack(劫持滚动);用 CSS scroll-snap 结合。测试快速滚动(anticipatePin)。
- 更新注意:3.12+ 支持 clamp() 边界;3.8+ 方向吸附和 containerAnimation(嵌套横向)。