同步更新至个人站点:GSAP ScrollTrigger 详解 - CellStack

在上一篇文章 GSAP 入门指南 里,我们学习了 GSAP 的两个核心:
- Tween:补间动画。
- Timeline:时间线。
有了它们,我们能让元素动起来。 但是,动画什么时候触发?靠谁来控制?
答案是:滚动(Scroll)。
最常见的场景:
- 元素滚动到视窗才开始播放。
- 滚动条走到哪里,动画精确跟到哪里。
- 内容卡住一会儿,再接着滚动,就像苹果官网。
要实现这些,我们需要今天的主角:ScrollTrigger 。 它是 GSAP 官方提供的滚动插件。 一句话:把滚动条变成动画的遥控器。
注册插件
我们还是用 CDN 的方式引入:
html
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
然后在 JS 里注册:
js
gsap.registerPlugin(ScrollTrigger)
不注册的话,GSAP 根本不知道有个滚动插件存在。
核心概念
ScrollTrigger 配置很多,但核心问题就两个:
- 什么时候开始?
- 怎么播放?
trigger
谁来触发?
js
gsap.to(".box", {
x: 500,
duration: 2,
scrollTrigger: {
trigger: ".box",
},
})
这里 .box
就是触发点。 当 .box
出现在视口,动画才会执行。
start 和 end
动画何时开始?何时结束?
格式:"<元素位置> <视窗位置>"
。
start: "top center"
→ 元素顶部到达视窗中心时开始。end: "+=300"
→ 从开始再滚动 300px,结束。
可以想象一根"滚动尺子",start
和 end
就是区间范围。
markers
调试神器。
js
scrollTrigger: {
trigger: ".box",
start: "top center",
markers: true
}
页面会出现彩色的 start
/ end
标记线。 建议开发时一直开着,肉眼确认动画区间。

scrub
ScrollTrigger 的灵魂。
- 默认:动画触发后,按
duration
播放完。 scrub: true
:动画进度和滚动条绑定。
js
scrub: true
滚动到一半,动画停在一半。 就像动画挂在滚动条上。
scrub: 1
→ 多加 1 秒缓冲,让体验更丝滑。
pin
pin
可以让元素在滚动区间内固定。
js
pin: true
这就是网页常见的"卡住"效果。 和 CSS 的 sticky
不同,它能和动画区间深度绑定,常见的横行滚动,就是这个效果。
toggleActions
如果不用 scrub
,就要靠 toggleActions
。
它控制四种状态:
onEnter
onLeave
onEnterBack
onLeaveBack
默认是 "play none none none"
:
- 元素进入时播放一次
- 其他情况不处理
如果你想"返回时反播",可以设置:
js
toggleActions: "play none none reverse"
实战演练
下面给出三个完整示例,复制到本地就能跑。 每个示例后我都会点出关键解释。
示例一:元素进入视窗
方块从左侧淡入。 进入视窗时播放,返回时反向。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>ScrollTrigger 示例1</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
<style>
body {
height: 300vh;
}
.spacer {
height: 100vh;
}
.box {
width: 150px;
height: 150px;
background: #28a92b;
margin-left: 50px;
}
</style>
</head>
<body>
<div class="spacer"></div>
<div class="box"></div>
<div class="spacer"></div>
<script>
gsap.registerPlugin(ScrollTrigger)
gsap.to(".box", {
x: 600,
y: 100,
rotation: 360,
duration: 2,
scrollTrigger: {
trigger: ".box",
start: "top 80%", // 元素进入视口下方 80% 时触发
end: "bottom 20%", // 元素离开视口上方 20% 时结束
toggleActions: "play none none reverse",
markers: true,
},
})
</script>
</body>
</html>
关键点:
toggleActions: "play ... reverse"
→ 往下滚时播放,往上滚时反播。markers: true
→ 方便调试触发区间。

示例二:视差滚动
背景比前景慢,制造 3D 深度感。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>ScrollTrigger 示例2</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
<style>
section {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-size: 4rem;
}
.section-two {
background: url("https://picsum.photos/1200/1200?random=1") no-repeat
center/cover;
height: 100vh;
}
</style>
</head>
<body>
<section class="section-one"><h1>第一页</h1></section>
<section class="section-two"></section>
<section class="section-three"><h1>结束页</h1></section>
<script>
gsap.registerPlugin(ScrollTrigger)
gsap.to(".section-two", {
backgroundPosition: "50% 100%",
scrollTrigger: {
trigger: ".section-two",
start: "top bottom", // 元素顶部到达视窗底部时开始
end: "bottom top", // 元素底部到达视窗顶部时结束
scrub: 1, // 滚动进度与动画绑定,+1秒缓冲
markers: true,
},
})
</script>
</body>
</html>
关键点:
scrub: 1
→ 背景跟着滚动,有点延迟,更真实。start
和end
→ 定义滚动区间,覆盖整个视差段落。

示例三:横向滚动
纵向滚动转为水平切换。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>ScrollTrigger 示例3</title>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
<style>
.horizontal-container {
height: 100vh;
overflow: hidden;
}
.panel-wrapper {
height: 100%;
display: flex;
}
.panel {
flex: 0 0 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 3rem;
}
.panel:nth-child(1) {
background: url("https://picsum.photos/1200/1200?random=1") center/cover;
}
.panel:nth-child(2) {
background: url("https://picsum.photos/1200/1200?random=2") center/cover;
}
.panel:nth-child(3) {
background: url("https://picsum.photos/1200/1200?random=3") center/cover;
}
.spacer {
height: 100vh;
}
</style>
</head>
<body>
<div class="spacer"></div>
<section class="horizontal-container">
<div class="panel-wrapper">
<div class="panel">第一页</div>
<div class="panel">第二页</div>
<div class="panel">第三页</div>
</div>
</section>
<div class="spacer"></div>
<script>
gsap.registerPlugin(ScrollTrigger)
const wrapper = document.querySelector(".panel-wrapper")
gsap.to(wrapper, {
x: () => -(wrapper.scrollWidth - window.innerWidth),
ease: "none",
scrollTrigger: {
trigger: ".horizontal-container",
pin: true, // 固定容器
scrub: 1, // 滚动驱动动画
end: () => "+=" + (wrapper.scrollWidth - window.innerWidth),
invalidateOnRefresh: true,
markers: true,
},
})
</script>
</body>
</html>
关键点:
pin: true
→ 容器在滚动期间固定住。x: () => -(wrapper.scrollWidth - window.innerWidth)
→ 根据内容宽度计算移动距离。invalidateOnRefresh: true
→ 窗口大小变化时,重新计算。

总结
ScrollTrigger 的本质是: 让滚动条变成动画的时间轴。
要点:
- 引入并注册插件。
- 用
trigger
、start
、end
控制触发区间。 - 开启
markers
调试。 scrub
、pin
是进阶玩法的核心。
有了它,你能轻松实现滚动叙事: 从淡入淡出,到视差,再到横向切换。
(完)