关于 gsap-scrolltrigger-pin
用于制作滚动时固定的横向多页区块,将纵向滚动映射为横向滑动。它规定了 GSAP ScrollTrigger、减少动态效果的处理及浏览器验证方式。
主要功能
- 01固定式横向幻灯片轨道
- 02GSAP ScrollTrigger 滚动联动
- 03减少动态效果时改用 CSS
- 04验证尺寸调整与横向溢出
适用场景
- 01制作横向滚动的作品集区块
- 02将产品图库改为固定滚动交互
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rheinmir/ setup --skill gsap-scrolltrigger-pin安装后在 Claude Code 中输入/gsap-scrolltrigger-pin确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/demo.htmlgsap-scrolltrigger-pin.cssgsap-scrolltrigger-pin.js详细介绍
技能:gsap-scrolltrigger-pin
滚动到此处时将区块固定在原位,ScrollTrigger 读取用户的垂直滚动,并将其转换为包含多张幻灯片的轨道水平滑动——这是业界标准的“固定式水平滚动区块”技术。
WHAT
目的和上下文
- **目的:**滚动到此处时固定一个区块,GSAP ScrollTrigger 将垂直滚动转换为多张幻灯片轨道的水平滑动;默认使用
Rheinmir/uiux-asset中的原始版本,不作改动。 - 触发条件(何时使用):
- 页面需要一个类似“横向画廊”的区块(案例研究、作品集、产品),同时用户仍像往常一样只滚动鼠标滚轮。
- 用户提到“固定区块”、“横向滚动”、“horizontal scroll section”、“scroll-driven horizontal”、“gsap scrolltrigger”或 /gsap-scrolltrigger-pin。
- **非目标:**不要用于无需固定的轻量滚动效果(使用
scroll-effects),也不要用于平滑整个页面的滚动体验(使用lenis-smooth-scroll)——这两个技能都不会引入 GSAP 依赖。仅用于一个特定的固定区块。
心智模型
[data-hp-section] > .hp-pin (pin) > [data-hp-track] (tween xPercent, scrub) > [data-hp-slide] × N;end 是一个函数,会在每次 refresh() 时重新测量 track.scrollWidth;matchMedia 负责选择 GSAP 分支或减少动态效果分支(普通 CSS 横向滚动)。
输入和输出约定
| 字段 | 必填? | 含义 | |
|---|---|---|---|
| In | 区块的幻灯片内容 | 是 | 案例研究 / 作品集 / 产品 |
| In | 是否允许引入外部库(GSAP) | 是 | 不允许引入库 → 使用精简版 assets/,但仍必须使用 GSAP |
| In | 项目的 design.md | 否 | 如有,则按其调整颜色 token |
| Out | 项目中的固定区块 + 水平轨道 | 是 | 原样复制源文件(保留署名注释)或改编 assets/ |
| Out | playwright-verify 验证证据 | 是 | 固定时机正确、无横向溢出、调整大小后仍正常 |
规则和能力
- RULE-01 (MUST):必须依赖 GSAP——缺少 GSAP 和 ScrollTrigger 插件时,此技能无法运行。以下两种方式任选其一:
- CDN(如 demo.html):
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>+.../ScrollTrigger.min.js,在技能脚本之前加载。 - npm:
npm install gsap,然后执行import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger);。
- CDN(如 demo.html):
- RULE-02 (MUST):
prefers-reduced-motion: reduce→ GSAP 分支不执行任何初始化(不使用 ScrollTrigger、不固定、不 scrub)——CSS 会自动将.hp-pin切换为overflow-x: auto的普通横向滚动(scroll-snap-type: x mandatory)。此分支中不要添加任何 JS 动画。 - RULE-03 (MUST):轨道使用
xPercent,不要使用x(px)——无论幻灯片内容如何改变尺寸,xPercent都会自动适配轨道的实际宽度,无需手动重新计算 px 偏移量。 - RULE-04 (MUST):使用
playwright-verify验证:垂直滚动穿过整个区块,断言区块在正确时机固定(轨道尚未播放完所有幻灯片时,视口不会继续垂直滚动);断言页面其余部分没有横向溢出;调整视口大小后重新滚动,确认ScrollTrigger.refresh()已适配新的布局。 - 能力:读取原始版本(web/git)、技能资源和
design.md;将 HTML/CSS/JS 写入目标项目;运行无头浏览器进行验证。
失败边界
- 缺少 GSAP + ScrollTrigger 且不允许添加依赖 → 受阻(RULE-01),如果实际需求并不需要固定效果,则建议使用
scroll-effects/lenis-smooth-scroll。 - 使用
file://打开文件 → 行为异常;必须通过 HTTP 在本地运行。 - 验证断言失败(未固定、横向溢出、调整大小后出现问题)→ 失败,修复后重新验证。
HOW
主要工作流
| 步骤 | 类型 | 输入 | 操作 | 输出/退出 | 失败/下一步 |
|---|---|---|---|---|---|
| W01 | 判断 | 需求 | 确认是否需要固定 + 水平滚动(不是 scroll-effects/lenis-smooth-scroll) | 选择 skill | 不需要固定 → 切换到其他 skill |
| W02 | 效果 | 原始 Pen | 打开运行链接,复制整个 .html 文件(+ _assets/)到项目中,保留署名注释 | 项目中的原始文件 | 不允许引入外部库 → B01 |
| W03 | 效果 | 文件 + design.md | 只修改内容/图片/颜色 | 已适配的 section | — |
| W04 | 确定性 | 通过 HTTP 运行的页面 | 使用 playwright-verify 验证 RULE-04 | 断言通过 | 未通过 → 修改并重复 W04 |
分支
| ID | 类型 | 条件 | 行为 | 跳过 / 失败 | 汇合 |
|---|---|---|---|---|---|
| B01 | conditional_required | 项目不允许引入外部库(原始版本) | 使用精简版 vanilla assets/(见 Reference — Assets),阅读后再适配 | 仍需要 GSAP(RULE-01) | W04 |
| B02 | conditional_required | prefers-reduced-motion: reduce | 不初始化 GSAP,使用普通 CSS 水平滚动(RULE-02) | — | W04 |
验证与停止
W04 中进行确定性断言(正确固定、无水平溢出、调整大小后再次滚动仍正确)。需要人工检查:内容/颜色符合项目风格。断言通过后停止;重复超过 2 轮仍未通过 → 附上截图告知用户。
示例
- 正例:“制作一个水平滚动的作品集 section” → 从
Rheinmir/uiux-asset复制horizontal-scroll-section-with-gsap-and-locomotive-scroll.html,更换图片/文字 → 使用 playwright 验证垂直滚动:section 固定,track 滑过所有 slide 后才释放,没有水平溢出。 - **边界/失败:**使用
x: -2000(px)代替xPercent→ 调整 viewport 大小后,track 偏移/最后一个 slide 显示不全 → 违反 RULE-03,改为xPercent: -100 * (slideCount - 1),然后再次验证调整大小后的效果。
参考 — 原始版本 — 优先使用(复制整个文件,不要重写)
作者的原始完整代码(公开的 CodePen = MIT)位于外部仓库 Rheinmir/uiux-asset — 可实际运行的 gallery:https://rheinmir.github.io/uiux-asset/scroll-effects/ 。
流程:打开运行链接查看实际效果 → 将整个 .html 文件(+ 文件引用的 _assets/ 目录)复制到项目中 → 保留首行署名注释 → 只修改内容/图片/颜色。通过 HTTP 在本地运行,不要使用 file://。
如果需要完整离线版本,可运行 git clone --depth 1 https://github.com/Rheinmir/uiux-asset。
| 原始 Pen — 作者 | 链接 |
|---|---|
| Horizontal Scroll Section with GSAP and Locomotive Scroll — cameronknight | 运行 · 代码 |
| Smooth Scroll Stacking Accordion — supah | 运行 · 代码 |
| Infinite Horizontal Scroll with Progress Tracking — haptichash | 运行 · 代码 |
| Pinned Split-Screen Mask Reveal — gridmorphic | 运行 · 代码 |
| Scroll-Driven SVG Map Editorial Gallery — knyttneve | 运行 · 代码 |
此 skill 中的 assets/ 仅为自行编写的精简版 vanilla 版本 — 仅在项目不允许引入外部库时使用;默认使用上方的原始版本。
参考 — 资源
assets/demo.html + assets/gsap-scrolltrigger-pin.css + assets/gsap-scrolltrigger-pin.js — 阅读后进行改编(如果项目有 design.md,则更换幻灯片内容和颜色 token)。直接用浏览器打开 demo.html,查看实际运行机制(资源通过 CDN 加载 GSAP)。
参考 — 机制
- 标记:
[data-hp-section] > .hp-pin > [data-hp-track] > [data-hp-slide] × N— track 为 flex-row,每张幻灯片flex: 0 0 100vw,总宽度 = N × 100vw(由布局自然计算,无需手动计算)。 ScrollTrigger固定.hp-pin(pin: true、start: "top top"),scrub: 1将动画进度锁定到对应的滚动位置——这不是随时间运行的动画。- 动画为
gsap.to(track, { xPercent: -100 * (slideCount - 1) })—slideCount从track.querySelectorAll('[data-hp-slide]').length读取,不在代码中硬编码幻灯片数量。 end是一个函数:() => "+=" + (track.scrollWidth - window.innerWidth)— 每次运行ScrollTrigger.refresh()时重新计算(根据当时 DOM 中 track 的实际scrollWidth重新测量),而不是一个固定值。- 用
ScrollTrigger.matchMedia()包裹整个初始化流程:当 viewport 切换断点或prefers-reduced-motion改变时,GSAP 会自动回滚旧分支并运行匹配的新分支——这是应对 resize 的标准方式,而不是手动编写 resize listener。 window.addEventListener('load', () => ScrollTrigger.refresh())用于处理图片/字体加载完成后 track 的实际scrollWidth才发生变化的情况。
来源
批次 180926 — 原始版本原封不动地位于 Rheinmir/uiux-asset/scroll-effects/(参见上文“原始版本”部分);assets/ 是自行编写的精简版原生实现。