GSAP ScrollTrigger 高级实战:复杂滚动动画的时间线编排

本文深入讲解 GSAP ScrollTrigger 在复杂滚动动画中的时间线编排,覆盖 timeline 与 scrollTrigger 的配合方式、containerAnimation 横向滚动场景、常见误区与方案选型,并给出工程落地建议。

散装触发的问题从哪来

做活动页或者产品落地页的时候,GSAP 加 ScrollTrigger 几乎是标准解法。滚动到一个区域,标题淡入,图片位移,数字滚起来,整个页面就像被一条隐形的导览线牵着走。但动画一多,问题就变了:不是写不出来,而是怎么让这些动画在滚动过程中既不打架、又好调整。这篇文章想聊的就是复杂滚动动画里的时间线编排。

AI technology illustration

所谓“复杂滚动动画”,我的定义通常是一个页面里同时存在多段呈现顺序、播放时长、滚动联动都不相同的动效。比如一个宣传页,顶部是轮播数据,往下是产品功能列表,再往下变成横向展示的大图卡片。这些模块彼此独立,却又要让用户感觉是在同一套节奏里。

很多早期滚动页面,会习惯给每个元素单独写一个 ScrollTrigger。动画少的时候这样很直白,可页面里一旦出现五六个区块,每个区块又有标题、描述、图片、背景装饰,你要同时维护十几个触发点。改一个元素的入场顺序,就得去翻不同 trigger 的 start、end,可能还要牵涉 toggleActions,往往越改越乱。

另一个典型问题隐藏在快速滚动里。用户滚得快的时候,各触发点的命中时间不一样,动画时长也不一样,视觉上就会出现一种非常散的感觉。单独看每个模块问题不大,但整页节奏明显是散的。为什么会这样?因为每个动画都只拿到了滚动的一部分进度,没有一条统一时间线串联它们。这就是复杂页面里值得用 timeline 的原因。

把动画交给一条时间线

ScrollTrigger 驱动 timeline 的原理其实很简单。你把整段动画写进一条 timeline,然后只给这条 timeline 挂上 ScrollTrigger。滚动进度和整段动画之间的映射关系就是一对一的,不用再关心每个动画是在哪个滚动百分比开始的。

const sceneTl = gsap.timeline({
  scrollTrigger: {
    trigger: "#feature-section",
    start: "top 70%",
    end: "bottom 10%",
    scrub: 1
  }
});

sceneTl
  .fromTo(".feature__title", { y: 80, autoAlpha: 0 }, { y: 0, autoAlpha: 1, duration: 0.8 })
  .fromTo(".feature__desc", { y: 40, autoAlpha: 0 }, { y: 0, autoAlpha: 1, duration: 0.6 }, "-=0.4")
  .fromTo(".feature__media", { scale: 1.1 }, { scale: 1, duration: 1.2 }, "<");

这段代码里,标题和描述不是严格按照 duration 数值相加去对齐,而是通过 -=0.4< 建立相对位置。< 表示“和前一个动画同时开始”,-=0.4 表示“比前一个动画结束时间提前 0.4 秒开始”。熟悉 timeline 位置参数的人应该清楚,这种碎片化编排才能真正把动画组织成有逻辑的序列。

scrub: 1 意味着滚动停止后,时间线还会用 1 秒去追赶目标状态,视觉上有种“粘着感”。对总时长比较长的复杂场景,一般设置在 0.8 到 2 之间比较自然。但要注意,太大会让动画明显跟不上手,并不是越大越跟手。

这样做的另一个好处是,以后想调整动画顺序,不需要去改每个 tween 触发区域,只需要调整 timeline 里的位置参数和 duration。ScrollTrigger 关心的只是整条时间线什么时候开始、什么时候结束。

进阶:横向滚动用 containerAnimation

横向滚动是时间线编排里最有挑战的一类。一个横向轨道本身用 ScrollTrigger 做 pin,然后 timeline 驱动轨道横向位移。此时轨道内部的卡片还要根据进入视口播放进入动画。很多人第一反应是给卡片也单独挂 ScrollTrigger,但卡片实际只在横向坐标上移动,主滚动条的坐标没有变化,两个触发点很容易互相打架。

正确做法是分两层:外层轨道用 timeline 驱动,内层动画的 ScrollTrigger 通过 containerAnimation 参数挂到外层 timeline 上。

const horizontalTl = gsap.timeline({
  scrollTrigger: {
    trigger: "#gallery",
    pin: true,
    scrub: 1,
    end: () => "+=" + (track.scrollWidth - window.innerWidth)
  }
});

gsap.utils.toArray(".gallery__card").forEach((card) => {
  gsap.fromTo(card, { x: 120, autoAlpha: 0 }, {
    x: 0,
    autoAlpha: 1,
    duration: 0.6,
    scrollTrigger: {
      trigger: card,
      containerAnimation: horizontalTl,
      start: "left 85%",
      toggleActions: "play none none reverse"
    }
  });
});

containerAnimation 会把内层卡片的触发时机映射到外层水平轨道的坐标系里。注意这里的 start、end 不再是浏览器可视区,而是横向容器的滚动位置。调试时别用普通 ScrollTrigger 的经验卡像素,用元素与容器的相对位置判断会稳得多。

这个场景不应该直接把所有卡片动画放进 horizontalTl 里面堆在一起。外层 timeline 负责轨道位移,内层卡片通过 containerAnimation 各自管理触发,内外两条线各自的职责非常清楚,查找问题也方便。

常见误区:时间线不能解决所有编排问题

时间线不是万能胶,以下三种情况很容易被带偏。

  • 把所有交互都塞进 timeline。hover 动画、鼠标跟随、数据更新导致的红点闪烁,这些和滚动没有直接关系。混进 timeline 后,一旦你要单独做跳过逻辑,会牵连其他无关动画。
  • 不理解 timeline 总时长和滚动范围的换算。ScrollTrigger 会把 start 和 end 的像素差平均分配给 timeline 的总时长。如果总时长 10 秒,某个动画只有 0.5 秒,它在滚动区间里就只占很小一段。想控制比例,要么调 duration,要么用 label 明确节点,而不是把所有动画都堆在一条线里靠运气。
  • 过早引入嵌套 timeline。嵌套 timeline 适合横滚区域这样有明显隔离的场景。如果只是顺序、交叠关系,一条扁平 timeline 加上 addLabel 完全够用。嵌套层级越多,越难判断当前正在播放的是哪一段。

三种编排方式怎么选

结合我自己的使用经验,三种方式之间并不是越高级越好,更多看页面复杂度。

组织方式 同步能力 可维护性 适合的场景
每个动画单独挂 ScrollTrigger 弱,各动画并行 差,触发点分散 页面只有一两处独立动效
单条 timeline 绑定一个 ScrollTrigger 强,节点可控 中,适合单个区块 功能模块内的连续滚动动画
多条 timeline 嵌套或组合 强,可分层 依赖设计,需先分层 横向滚动、多场景复杂编排

表格里的“单独挂 ScrollTrigger”并不代表不能用,而是它更适合动效数量少、彼此没有强依赖的场景。一旦需要多个动画同时推进或者顺序错位配合,就值得用时间线把这个协调工作交给代码结构。

落地清单

  1. 先在页面里画滚动动效草稿,标出每个动画进入点和退出点,给每个节奏块起名字,不要直接开工。
  2. 每个区块内部用一条时间线,区块之间不强行合并。合并得太狠,改一个区块会波及另一块。
  3. 时间线里的关键节点用 addLabel 标记,后续用 label 定位,避免魔法数字。
  4. 在 defaults 里设置统一的 ease,比如 ease: "power2.out",减少各 tween 各自为政。
  5. 异步内容加载完成或图片插入后,记得调用 ScrollTrigger.refresh(),否则基于旧布局算出的 start 和 end 会脱节。
  6. 属性尽量用 transform 和 opacity,减少 top、left 这类触发重排的属性,滚动动画密集时对帧率影响明显。

最后

时间线编排的真正作用,是把散落的动效收敛成可描述、可推理的播放顺序。滚动是用户输入,时间线负责把这个输入翻译成页面的视觉叙事。对于超过两三处联动的动效,我都建议先画一条时间线,不是迷信它,而是它逼你把动画之间的时间关系讲清楚。

GSAP 和 ScrollTrigger 的参数还有很多可玩的空间,但撑起整个体验的核心,始终是你能不能把时间关系想明白。写代码之前花十几分钟把 timeline 草图理顺,比之后反复调试十几个触发点划算得多。

原创文章,作者:fudengji,如若转载,请注明出处:https://fudengji.cn/article/918/

(0)
上一篇 14小时前
下一篇 6小时前

相关推荐