mcpField
G
gsap-web

gsap-web

github.com/iart-ai设计与 UI632.6k 次安装18 周前更新
代码仓库

关于 gsap-web

gsap-web 是面向网页开发的 GSAP 动画技能,提供时间轴编排、滚动动画、文字揭示和布局过渡的实现与调试指导,也涵盖独立 HTML 动画的验证流程。

主要功能

  1. 01GSAP 时间轴与精确编排
  2. 02ScrollTrigger 滚动动画
  3. 03SplitText 文字揭示
  4. 04Flip 布局过渡
  5. 05动画清理与性能建议
  6. 06独立 HTML 截图验证流程

适用场景

  1. 01制作滚动驱动的网页叙事
  2. 02编排多元素首屏动画
  3. 03实现文字分行揭示和布局切换
  4. 04调试平滑滚动与 ScrollTrigger 同步

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/iart-ai/web-animation-skills --skill gsap-web

安装后在 Claude Code 中输入/gsap-web确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
examples/
hero-timeline.js
standalone-template.html
references/
scrolltrigger-cookbook.md
scrolltrigger-lenis.md

详细介绍

面向 Web 的 GSAP

GSAP(GreenSock Animation Platform)是代码驱动 Web 动效的主力工具:可创建序列时间线、滚动驱动的叙事效果、文本显现效果和布局过渡效果。从 GSAP 3.12+ 起,所有插件(ScrollTrigger、SplitText、Flip、MotionPath、MorphSVG、Draggable、Observer)均可 100% 免费使用,包括商业用途。

适用场景

  • 滚动驱动的叙事效果:固定区段、视差、进度拖动、水平滚动
  • 具有精确重叠控制的序列式首屏动画和复杂多元素时间线
  • 将文本拆分为字符、单词或行后逐一显现(SplitText)
  • 元素位置、大小或父级发生变化时的布局过渡(Flip)
  • 任何需要精细时间控制、精准缓动或命令式编排的动效

安装和注册

import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { SplitText } from "gsap/SplitText";
import { Flip } from "gsap/Flip";

gsap.registerPlugin(ScrollTrigger, SplitText, Flip);

使用前必须注册插件,否则插件会静默失效。使用打包工具时,请在应用入口处注册一次。

核心技巧

优先使用时间线

相比多个互不相关的补间动画,优先使用一个时间线——它提供统一的播放头、相对定位和便捷的反向播放。

const tl = gsap.timeline({ defaults: { ease: "power3.out", duration: 0.6 } });
tl.from(".title", { yPercent: 100, opacity: 0 })
  .from(".sub",   { y: 20, opacity: 0 }, "-=0.3")  // start 0.3s before prev ends
  .from(".cta",   { scale: 0.9, opacity: 0 }, "<"); // align to prev tween START

位置参数速查:

  • "+=0.5" / "-=0.3" —— 相对于时间线末尾的位置(间隔 / 重叠)
  • "<" —— 上一个补间动画的起点;">" —— 上一个补间动画的终点
  • "<0.2" —— 上一个补间动画开始后 0.2 秒
  • "myLabel" —— 位于通过 tl.addLabel("myLabel") 添加的命名标签处

入场动画使用 .from()(从给定值动画到当前 CSS 值),退场动画使用 .to(),两端都必须明确指定时使用 .fromTo()(最能避免重复运行时出现问题)。

gsap.to、set 和 quickTo 的区别
gsap.set(el, { autoAlpha: 0 });           // instant, no tween (autoAlpha = opacity + visibility)
const xTo = gsap.quickTo(el, "x", { duration: 0.4, ease: "power3" });
window.addEventListener("pointermove", (e) => xTo(e.clientX)); // fast repeated updates

相比直接使用 opacity,更推荐使用 autoAlpha,因为它还会在值为 0 时切换 visibility:hidden,使元素不再参与命中测试。

ScrollTrigger 基础
gsap.to(".panel", {
  xPercent: -100,
  ease: "none",
  scrollTrigger: {
    trigger: ".wrap",
    start: "top top",      // when trigger top hits viewport top
    end: "+=2000",         // 2000px of scroll distance
    pin: true,             // freeze .wrap while the tween plays
    scrub: 1,              // tie progress to scrollbar (1 = 1s catch-up smoothing)
    markers: true,         // dev-only visual markers — remove for prod
  },
});

关键语义:

  • start/end 接受 "triggerPos viewportPos"(例如 "top center")或 "+=px"。
  • scrub: true 会使动画进度与滚动位置完全同步;scrub: <number> 则会增加平滑延迟。
  • 对于非 scrub 触发器,toggleActions: "play pause resume reverse" 用于控制 onEnter/onLeave/onEnterBack/onLeaveBack 时的行为。
  • 对于 scrub 补间动画,使用 ease: "none",让动效随滚动线性变化。
平滑滚动(Lenis)+ ScrollTrigger

将 Lenis(或 Locomotive)的平滑滚动与 ScrollTrigger 配合使用时,如果两者不在同一个循环中运行,就会导致不同步。Lenis 在自己的 rAF 中平滑滚动,而 ScrollTrigger 则通过 GSAP 的 ticker 读取滚动位置;如果两者独立运行,ScrollTrigger 读取到的就是过时的位置。解决方法:通过 GSAP 的 ticker 驱动 Lenis,并在每次 Lenis 滚动时更新 ScrollTrigger。

import Lenis from "lenis";

const lenis = new Lenis({ duration: 1.2, smoothWheel: true });

lenis.on("scroll", ScrollTrigger.update);          // 1. update ScrollTrigger on every Lenis scroll
gsap.ticker.add((t) => lenis.raf(t * 1000));       // 2. one loop: ticker drives Lenis (seconds → ms)
gsap.ticker.lagSmoothing(0);                        // 3. stop GSAP catch-up jitter on heavy frames

重要:gsap.ticker 传递的时间单位是秒,而 lenis.raf() 需要的是毫秒——请乘以 1000。不要再为 Lenis 单独运行 requestAnimationFrame(raf) 循环,否则会重复驱动。

常见问题:

  • 抖动 / 卡顿——残留的 requestAnimationFrame(raf) 循环与 ticker 争抢,或缺少 lagSmoothing(0)。移除多余循环,并将滞后平滑设为 0。
  • 标记与触发器位置偏移——未将 ScrollTrigger.update 订阅到 lenis.on("scroll", …)。
  • 固定元素异常——使用 Lenis 时不要设置 scrollerProxy(它滚动的是文档)。使用 Locomotive 时必须连接 scrollerProxy 和 pinType,并在每个触发器上设置 scroller:。
  • 滚动卡住 / 方向颠倒——缺少 * 1000 的单位转换,或两个循环的运行顺序错误。

在 SPA 卸载时清理:调用 gsap.ticker.remove(update) + lenis.destroy(),否则每次导航都会叠加循环。根据 prefers-reduced-motion 设置门控(跳过 Lenis,使用原生滚动)。完整的 Lenis + Locomotive 接线配置、锚点链接路由、data-lenis-prevent、React(useGSAP/useEffect),以及症状→原因对照表,详见 references/scrolltrigger-lenis.md。

SplitText(文本显现)
const split = SplitText.create(".headline", { type: "lines, words", linesClass: "line" });
gsap.from(split.lines, { yPercent: 100, opacity: 0, stagger: 0.08, duration: 0.7, ease: "power4.out" });

注意事项:

  • 对行遮罩显现效果,将 overflow: hidden 设置在行容器上,这样 yPercent: 100 才能将内容干净地隐藏起来。添加 autoSplit: true(GSAP 3.13+),以便在字体加载或窗口大小调整时重新拆分。
  • 重新拆分或卸载时,先调用 split.revert(),以还原原始 DOM 并避免节点重复。
  • 始终在网页字体加载后再拆分(document.fonts.ready.then(...)),以免出现错误的换行。
Flip(布局过渡)

Flip 会记录状态,让 DOM 立即变化,然后为视觉差异添加动画(FLIP 技术)。非常适合网格<->列表、展开卡片和重新挂载元素。

const state = Flip.getState(".item");   // 1. capture BEFORE
container.classList.toggle("grid");      // 2. mutate DOM/CSS (instant)
Flip.from(state, {                        // 3. animate the delta
  duration: 0.6, ease: "power2.inOut", stagger: 0.05,
  absolute: true,                         // take items out of flow during move (prevents reflow jitter)
});
缓动速查
  • power2/3.out — 入场(先快后缓)
  • power2.inOut — 在两个屏幕状态之间移动
  • back.out(1.7) — 超出后回弹(数字表示超出幅度)
  • elastic.out(1, 0.3) — 弹性效果,请谨慎使用
  • none — 由滚动控制的补间动画
  • steps(n) — 精灵图/阶梯式运动
  • 自定义三次贝塞尔曲线等效写法:CustomEase.create("x", "M0,0 C0.2,0 0,1 1,1")(CustomEase 免费)

性能与清理

  • 仅为 transform(x/y/xPercent/scale/rotation)和 opacity 添加动画——它们由 GPU 合成,可跳过布局/绘制。避免为 top/left/width/height 添加动画。
  • 为占用资源较多或固定定位的元素设置 will-change: transform;完成后移除。
  • 使用 ScrollTrigger.batch() 批量处理多个相似的滚动显现效果,而不是为每个元素单独创建触发器。
  • 视口或内容发生变化后,调用 ScrollTrigger.refresh()。

SPA / 框架中的清理是必需的——遗留的触发器会导致内存泄漏和幽灵固定效果。使用 gsap.context()(或 @gsap/react 中的 useGSAP):

useEffect(() => {
  const ctx = gsap.context(() => {
    // all gsap + ScrollTrigger code here
  }, rootRef);
  return () => ctx.revert(); // kills tweens, triggers, and reverts inline styles
}, []);

交付与验证(独立 HTML)

打包的辅助脚本(scripts/):scripts/seek-shot.sh anim.html 0 1.5 3 会冻结 ?t=N 测试工具并截取各时刻的画面;scripts/contact-sheet.sh sheet.png frame-*.png 会将它们拼成一张图,便于快速检查。参见 scripts/README.md。

对于自包含的动画(主视觉、显现效果、循环、微场景),交付物应是一个可直接在浏览器中打开的 HTML 文件——无需构建步骤、框架或渲染流水线。交付形式应与工作量相匹配:单文件是网页动效的合适层级;一个文件足以完成工作时,不要使用打包工具。

输出要求:

  • 一个 .html 文件:GSAP 和插件来自 CDN,包含你的标记和一个内联 <script> 中的动画。
  • 所有动效都放在一个主时间轴上(const tl = gsap.timeline())——使用单一播放头即可跳转。
  • 加入下方的跳转测试工具,以便冻结任意时刻进行检查。

跳转测试工具——冻结精确时刻以便截图。 这是视频播放器定格某一帧的网页对应做法:?t=N 会将主时间轴跳转到第 N 秒并暂停,让截图落在静止且确定的画面上。

<script>
  // ... build your master timeline as `tl` ...
  const t = new URLSearchParams(location.search).get("t");
  if (t !== null) { tl.pause(); tl.seek(parseFloat(t)); }  // frozen at t seconds
  // no ?t → plays normally
  window.__ready = true;            // ready signal for headless wait
  console.log("duration", tl.duration());
</script>

验证循环——渲染 → 冻结 → 截图 → 检查:

  1. 在时间轴的三个时刻打开文件——开始、中间、结束: …/anim.html?t=0、?t=<dur/2>、?t=<dur>。在控制台读取 tl.duration(),获取结束时间。
  2. 为每个冻结的画面截图。
  3. 检查还原度(是否符合需求?)和瑕疵(文本被裁切、元素超出画布、字体加载前出现 FOUC、衔接处卡顿)。输出效果应当自然且完善。

任何无头截图工具都可以使用——Agent 的浏览器工具或 Playwright:

npx playwright screenshot --wait-for-timeout=500 "file://$PWD/anim.html?t=1.2" frame-mid.png

完成前请确认:

  1. 可在浏览器中独立打开——没有控制台错误,也没有缺失的 CDN 资源。
  2. 所有动画都在一个主时间轴上;?t=N 能正确冻结画面。
  3. 已截取开始/中间/结束时刻的画面——符合需求且没有瑕疵。
  4. 已遵循 prefers-reduced-motion(简化或跳过时间轴)。
  5. 缓动设置合理——空间运动没有意外使用 linear。

包含完整可运行模板且已接入测试工具的文件位于 examples/standalone-template.html。

快速参考

目标API
带重叠的序列tl.from(...).from(..., "-=0.3")
滚动时固定scrollTrigger:{ pin:true, end:"+=N" }
随滚动擦动scrollTrigger:{ scrub:1, ease:"none" }
显示文本行SplitText.create(el,{type:"lines"})
为布局变化添加动画Flip.getState → mutate → Flip.from
快速跟随指针gsap.quickTo(el,"x",{...})
全部清除ScrollTrigger.getAll().forEach(t=>t.kill())

参考文件

  • references/scrolltrigger-cookbook.md — 固定、擦动、水平滚动、吸附、ScrollTrigger.batch() 显示效果、视差、嵌套触发器和 SPA 清理模式,附完整代码。
  • references/scrolltrigger-lenis.md — 完整的 ScrollTrigger + Lenis/Locomotive 平滑滚动同步:规范接线方式、scrollerProxy、锚点链接、减少动态效果、React(useGSAP/useEffect)、调试检查清单,以及症状→原因对照表。
  • examples/hero-timeline.js — 完整且可运行的 hero 入场时间线,包含 SplitText、交错显示效果和减少动态效果处理。
  • examples/standalone-template.html — 交付模板:独立运行(通过 CDN 加载 GSAP)、单个主时间线、用于截图验证的 ?t=N 定位测试工具,以及减少动态效果处理。

评论

支持 Markdown