GSAP ScrollTrigger 详解

同步更新至个人站点: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 配置很多,但核心问题就两个:

  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 是进阶玩法的核心。

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

(完)

相关推荐
gnip2 小时前
Node.js 子进程:child_process
前端·javascript
databook3 小时前
Manim实现脉冲闪烁特效
后端·python·动效
excel5 小时前
为什么在 Three.js 中平面能产生“起伏效果”?
前端
excel6 小时前
Node.js 断言与测试框架示例对比
前端
天蓝色的鱼鱼7 小时前
前端开发者的组件设计之痛:为什么我的组件总是难以维护?
前端·react.js
codingandsleeping7 小时前
使用orval自动拉取swagger文档并生成ts接口
前端·javascript
石金龙8 小时前
[译] Composition in CSS
前端·css
白水清风8 小时前
微前端学习记录(qiankun、wujie、micro-app)
前端·javascript·前端工程化
Ticnix9 小时前
函数封装实现Echarts多表渲染/叠加渲染
前端·echarts