Cell Stack

← 返回·

GSAP ScrollTrigger 详解

深入学习 GSAP ScrollTrigger 插件,掌握如何通过滚动控制动画,实现视差滚动、元素固定、滚动驱动动画等高级交互效果。

038.png

在上一篇文章 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 配置很多,但核心问题就两个:

  1. 什么时候开始?
  2. 怎么播放?

trigger

谁来触发?

js
gsap.to('.box', {  x: 500,  duration: 2,  scrollTrigger: {    trigger: '.box',  },})

这里 .box 就是触发点。 当 .box 出现在视口,动画才会执行。

start 和 end

动画何时开始?何时结束?

格式:"<元素位置> <视窗位置>"

  • start: "top center" → 元素顶部到达视窗中心时开始。
  • end: "+=300" → 从开始再滚动 300px,结束。

可以想象一根“滚动尺子”,startend 就是区间范围。

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 → 背景跟着滚动,有点延迟,更真实。
  • startend → 定义滚动区间,覆盖整个视差段落。

示例三:横向滚动

纵向滚动转为水平切换。

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 的本质是: 让滚动条变成动画的时间轴

要点:

  • 引入并注册插件。
  • triggerstartend 控制触发区间。
  • 开启 markers 调试。
  • scrubpin 是进阶玩法的核心。

有了它,你能轻松实现滚动叙事: 从淡入淡出,到视差,再到横向切换。

(完)


留言讨论