gsap-scrolltrigger-pin
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSkill: gsap-scrolltrigger-pin
Skill: gsap-scrolltrigger-pin
Section pin lại tại chỗ khi cuộn tới, cuộn dọc của người dùng được ScrollTrigger đọc và tween thành trượt ngang của một track chứa nhiều slide — kỹ thuật "pinned horizontal-scroll section" chuẩn ngành.
当用户滚动到某个 section 时,该 section 会固定在原位;ScrollTrigger 读取用户的纵向滚动,并将其 tween 为包含多个 slide 的 track 的横向位移——这是业界标准的“pinned horizontal-scroll section”技术。
WHAT
WHAT(内容)
Purpose và context
目的与上下文
- Purpose: một section pin lại khi cuộn tới, cuộn dọc được GSAP ScrollTrigger chuyển thành trượt ngang của track nhiều slide; mặc định dùng bản gốc nguyên văn ở .
Rheinmir/uiux-asset - Trigger (when to use):
- Trang cần một section kiểu "gallery ngang" (case study, portfolio, sản phẩm) mà người dùng vẫn chỉ lăn chuột dọc như bình thường.
- User nói "pin section", "cuộn ngang", "horizontal scroll section", "scroll-driven horizontal", "gsap scrolltrigger", hoặc /gsap-scrolltrigger-pin.
- Non-goals: KHÔNG dùng cho hiệu ứng cuộn nhẹ không cần pin (dùng ) hay làm mượt cảm giác cuộn toàn trang (dùng
scroll-effects) — hai skill đó KHÔNG kéo theo dependency GSAP. Chỉ áp dụng cho MỘT section pin cụ thể.lenis-smooth-scroll
- Purpose: 在用户滚动到某个 section 时将其固定住,GSAP ScrollTrigger 会将纵向滚动转换为包含多个 slide 的 track 的横向滑动;默认使用 中原封不动的原始版本。
Rheinmir/uiux-asset - 触发条件(何时使用):
- 页面需要采用“横向 gallery”形式的 section(case study、portfolio、产品),同时用户仍只需像平常一样纵向滚动鼠标。
- 用户说出“pin section”“cuộn ngang”“horizontal scroll section”“scroll-driven horizontal”“gsap scrolltrigger”或 /gsap-scrolltrigger-pin。
- 非适用场景: 不用于无需 pin 的轻量级滚动效果(使用 ),也不用于让整页滚动更顺滑(使用
scroll-effects)——这两个 skill 均不会引入 GSAP dependency。此 skill 只适用于一个特定的待 pin section。lenis-smooth-scroll
Mental model
心智模型
[data-hp-section] > .hp-pin (pin) > [data-hp-track] (tween xPercent, scrub) > [data-hp-slide] × Nendtrack.scrollWidthrefresh()matchMedia[data-hp-section] > .hp-pin (pin) > [data-hp-track] (tween xPercent, scrub) > [data-hp-slide] × Nendrefresh()track.scrollWidthmatchMediaInput và output contract
输入与输出约定
| Field | Required? | Ý nghĩa | |
|---|---|---|---|
| In | nội dung slide của section | có | case study / portfolio / sản phẩm |
| In | quyền kéo lib ngoài (GSAP) | có | không được kéo lib → dùng bản rút gọn |
| In | | không | có thì đổi token màu theo nó |
| Out | section pin + track ngang trong dự án | có | copy nguyên file gốc (giữ comment ghi công) hoặc adapt |
| Out | bằng chứng verify | có | pin đúng lúc, không overflow ngang, sống sót resize |
| 字段 | 必填? | 含义 | |
|---|---|---|---|
| 输入 | section 的 slide 内容 | 是 | case study / portfolio / 产品 |
| 输入 | 引入外部库(GSAP)的权限 | 是 | 不允许引入 lib → 使用精简版 |
| 输入 | 项目的 | 否 | 如果存在,可根据它调整颜色 token |
| 输出 | 项目中的 pinned section + 横向 track | 是 | 复制完整的原始文件(保留署名 comment),或适配 |
| 输出 | | 是 | pin 时机正确、无横向溢出,并且能够适应 resize |
Rules và capabilities
规则与能力
- RULE-01 (MUST): Phụ thuộc GSAP là bắt buộc — skill này KHÔNG chạy nếu thiếu GSAP + plugin ScrollTrigger. Cài một trong hai cách:
- CDN (như demo.html): +
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>, load TRƯỚC script của skill..../ScrollTrigger.min.js - npm: , rồi
npm install gsap.import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger);
- CDN (như demo.html):
- RULE-02 (MUST): → nhánh GSAP KHÔNG init gì cả (không ScrollTrigger, không pin, không scrub) — CSS tự chuyển
prefers-reduced-motion: reducethành.hp-pincuộn ngang thường (overflow-x: auto). Đừng thêm animation JS nào trong nhánh này.scroll-snap-type: x mandatory - RULE-03 (MUST): Track dùng , không dùng
xPercent(px) —xtự bám theo width thật của track dù nội dung slide đổi kích thước, không cần tính lại offset px tay.xPercent - RULE-04 (MUST): Verify bằng : cuộn dọc qua hết section, assert section pin đúng lúc (viewport không cuộn dọc thêm khi track chưa hết slide), assert không có horizontal overflow ở phần còn lại của trang, test resize viewport rồi cuộn lại để chắc
playwright-verifyđã bắt layout mới.ScrollTrigger.refresh() - Capabilities: đọc bản gốc (web/git) + asset skill + ; ghi HTML/CSS/JS vào dự án đích; chạy trình duyệt headless để verify.
design.md
- RULE-01(MUST):依赖 GSAP 是强制要求——缺少 GSAP + ScrollTrigger 时,此 skill 无法运行。可通过以下任一方式安装:
- CDN(例如 demo.html):+
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>,必须在 skill 脚本之前加载。.../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):当 时,GSAP 分支不进行任何初始化(无 ScrollTrigger、无 pin、无 scrub)——CSS 会自动将
prefers-reduced-motion: reduce转换为带.hp-pin的常规横向滚动(overflow-x: auto)。不要在此分支中添加任何 JS 动画。scroll-snap-type: x mandatory - RULE-03(MUST):Track 必须使用 ,不能使用
xPercent(px)——x会自动跟随 track 的实际宽度,即使 slide 内容尺寸发生变化,也无需手动重新计算 px offset。xPercent - RULE-04(MUST):必须使用 验证:纵向滚过整个 section,断言 section 在恰当时机被 pin(当 track 尚未移动完所有 slide 时,viewport 不得继续纵向滚动);断言页面剩余部分不存在 horizontal overflow;测试 resize viewport 后再次滚动,以确认
playwright-verify已获取新的 layout。ScrollTrigger.refresh() - 能力:读取原始版本(web/git)+ skill asset + ;将 HTML/CSS/JS 写入目标项目;运行 headless browser 进行验证。
design.md
Failure boundaries
失败边界
- Thiếu GSAP + ScrollTrigger và không được thêm dependency → blocked (RULE-01), đề xuất /
scroll-effectsnếu yêu cầu thật ra không cần pin.lenis-smooth-scroll - Mở file bằng → hành vi không đúng; phải chạy local qua HTTP.
file:// - Assert verify đỏ (không pin, overflow ngang, resize vỡ) → failed, sửa rồi verify lại.
- 缺少 GSAP + ScrollTrigger 且不允许添加 dependency → blocked(RULE-01);如果实际需求并不需要 pin,建议改用 或
scroll-effects。lenis-smooth-scroll - 通过 打开文件 → 行为不正确;必须通过 HTTP 运行本地服务。
file:// - 验证断言失败(未 pin、出现 horizontal overflow、resize 后布局损坏)→ failed;修复后重新验证。
HOW
HOW(流程)
Main workflow
主流程
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|---|---|---|---|---|
| W01 | judgment | yêu cầu | Xác nhận cần pin + cuộn ngang (không phải | chọn skill | không cần pin → chuyển skill khác |
| W02 | effect | bảng Pen gốc | Mở link chạy, copy nguyên file | file gốc trong dự án | không được kéo lib ngoài → B01 |
| W03 | effect | file + | Chỉ sửa nội dung/ảnh/màu | section đã adapt | — |
| W04 | deterministic | trang chạy qua HTTP | Verify RULE-04 bằng | assert xanh | đỏ → sửa, lặp W04 |
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|---|---|---|---|---|
| W01 | judgment | 用户需求 | 确认需要 pin + 横向滚动(而非 | 选择 skill | 不需要 pin → 切换到其他 skill |
| W02 | effect | 原始 Pen | 打开可运行的链接,将完整的 | 项目中包含原始文件 | 不允许引入外部 lib → B01 |
| W03 | effect | 文件 + | 仅修改内容、图片和颜色 | 已适配的 section | — |
| W04 | deterministic | 通过 HTTP 运行的项目页面 | 使用 | 断言通过 | 失败 → 修复并重复 W04 |
Branches
分支
| ID | Kind | Guard | Hành vi | Skip / failure | Rejoin |
|---|---|---|---|---|---|
| B01 | conditional_required | dự án KHÔNG được kéo lib ngoài (bản gốc) | dùng bản rút gọn vanilla | vẫn cần GSAP (RULE-01) | W04 |
| B02 | conditional_required | | không init GSAP, CSS cuộn ngang thường (RULE-02) | — | W04 |
| ID | Kind | Guard | 行为 | Skip / failure | Rejoin |
|---|---|---|---|---|---|
| B01 | conditional_required | 项目不允许引入外部 lib(原始版本) | 使用自行编写的精简 vanilla | 仍然需要 GSAP(RULE-01) | W04 |
| B02 | conditional_required | | 不初始化 GSAP,改用常规 CSS 横向滚动(RULE-02) | — | W04 |
Validation và stopping
验证与停止条件
Assert tất định ở W04 (pin đúng lúc, không overflow ngang, resize rồi cuộn lại vẫn đúng). Cần mắt: nội dung/màu hợp dự án. Dừng khi assert xanh; lặp quá 2 vòng vẫn đỏ → báo user kèm ảnh.
在 W04 处执行确定性断言(pin 时机正确、无 horizontal overflow、resize 后再次滚动仍然正确)。还需要目测检查:内容和颜色是否符合项目要求。断言通过时停止;连续超过 2 轮仍失败,则向用户报告并附上截图。
Examples
示例
- Positive: "làm section portfolio cuộn ngang" → copy từ
horizontal-scroll-section-with-gsap-and-locomotive-scroll.html, đổi ảnh/chữ → playwright cuộn dọc: section pin, track trượt hết slide rồi mới nhả, không overflow ngang.Rheinmir/uiux-asset - Boundary/failure: dùng (px) thay
x: -2000→ đổi kích thước viewport thì track lệch/hụt slide cuối → vi phạm RULE-03, đổi sangxPercentrồi verify resize lại.xPercent: -100 * (slideCount - 1)
- Positive:“制作一个 portfolio 横向滚动 section”→ 从 复制
Rheinmir/uiux-asset,替换图片和文字 → 使用 playwright 纵向滚动:section 被 pin,track 滑完所有 slide 后才释放,且不存在 horizontal overflow。horizontal-scroll-section-with-gsap-and-locomotive-scroll.html - **边界/失败:**使用 (px)代替
x: -2000→ 改变 viewport 尺寸后,track 会偏移或漏掉最后一个 slide → 违反 RULE-03;改用xPercent,然后重新执行 resize 验证。xPercent: -100 * (slideCount - 1)
Reference — Bản gốc — dùng TRƯỚC (copy nguyên file, đừng viết lại)
Reference — 原始版本 — 请先使用(复制完整原始文件,不要重写)
Code gốc NGUYÊN VĂN của tác giả (CodePen public = MIT) nằm ở repo ngoài — gallery chạy thật: https://rheinmir.github.io/uiux-asset/scroll-effects/ .
Quy trình: mở link chạy để xem đúng hiệu ứng → copy nguyên file (+ thư mục nếu file trỏ tới) vào dự án → giữ comment ghi công dòng đầu → chỉ sửa nội dung/ảnh/màu. Chạy local qua HTTP, không .
nếu cần cả bộ offline.
Rheinmir/uiux-asset.html_assets/file://git clone --depth 1 https://github.com/Rheinmir/uiux-asset| Pen gốc — tác giả | Link |
|---|---|
| Horizontal Scroll Section with GSAP and Locomotive Scroll — cameronknight | chạy · code |
| Smooth Scroll Stacking Accordion — supah | chạy · code |
| Infinite Horizontal Scroll with Progress Tracking — haptichash | chạy · code |
| Pinned Split-Screen Mask Reveal — gridmorphic | chạy · code |
| Scroll-Driven SVG Map Editorial Gallery — knyttneve | chạy · code |
assets/作者提供的原封不动的原始代码(CodePen public = MIT)位于外部仓库 ——可直接运行的 gallery:https://rheinmir.github.io/uiux-asset/scroll-effects/ 。
流程:打开可运行的链接以查看准确效果 → 将完整的 文件(若文件引用了 ,则一并复制)复制到项目 → 保留首行署名 comment → 仅修改内容、图片和颜色。通过 HTTP 在本地运行,不能使用 。
如果需要完整的离线版本,可执行 。
Rheinmir/uiux-asset.html_assets/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 中的 只是自行编写的精简 vanilla 版本——当项目不允许引入外部 lib 时使用;默认使用上述原始版本。
assets/Reference — Assets
Reference — Assets
assets/demo.htmlassets/gsap-scrolltrigger-pin.cssassets/gsap-scrolltrigger-pin.jsdesign.mddemo.htmlassets/demo.htmlassets/gsap-scrolltrigger-pin.cssassets/gsap-scrolltrigger-pin.jsdesign.mddemo.htmlReference — Cơ chế
Reference — 实现机制
- Markup: — track là flex-row, mỗi slide
[data-hp-section] > .hp-pin > [data-hp-track] > [data-hp-slide] × N, tổng width = N × 100vw (tự nhiên theo layout, không tính tay).flex: 0 0 100vw - pin
ScrollTrigger(.hp-pin,pin: true),start: "top top"khoá tiến trình animation vào đúng vị trí cuộn — không phải animation chạy theo thời gian.scrub: 1 - Animation là —
gsap.to(track, { xPercent: -100 * (slideCount - 1) })đọc từslideCount, không hardcode số slide trong code.track.querySelectorAll('[data-hp-slide]').length - là một HÀM:
end— tính lại mỗi lần() => "+=" + (track.scrollWidth - window.innerWidth)chạy (đo lại theoScrollTrigger.refresh()thật của DOM tại thời điểm đó), không phải một số cố định.track.scrollWidth - bọc toàn bộ init: khi viewport đổi breakpoint hoặc
ScrollTrigger.matchMedia()đổi, GSAP tự revert nhánh cũ và chạy lại nhánh khớp — đây là cách chuẩn để "sống sót" qua resize, thay vì tự viết resize listener tay.prefers-reduced-motion - bắt trường hợp ảnh/font load xong mới đổi
window.addEventListener('load', () => ScrollTrigger.refresh())thật của track.scrollWidth
- Markup:——track 使用
[data-hp-section] > .hp-pin > [data-hp-track] > [data-hp-slide] × N,每个 slide 设置flex-row,总 width = N × 100vw(自然跟随 layout,无需手动计算)。flex: 0 0 100vw - 会 pin
ScrollTrigger(.hp-pin、pin: true),而start: "top top"会将动画进度锁定到准确的滚动位置——它不是按时间运行的动画。scrub: 1 - 动画为 ——
gsap.to(track, { xPercent: -100 * (slideCount - 1) })从slideCount读取,不能在代码中 hardcode slide 数量。track.querySelectorAll('[data-hp-slide]').length - 是一个函数:
end——每次() => "+=" + (track.scrollWidth - window.innerWidth)运行时都会重新计算(在当时的 DOM 中测量真实的ScrollTrigger.refresh()),而不是使用固定数值。track.scrollWidth - 包裹整个初始化过程:当 viewport 跨越 breakpoint 或
ScrollTrigger.matchMedia()发生变化时,GSAP 会自动 revert 旧分支并运行匹配的新分支——这是通过 resize 保持功能正常的标准方式,无需手动编写 resize listener。prefers-reduced-motion - 用于处理图片或字体加载完成后,track 的实际
window.addEventListener('load', () => ScrollTrigger.refresh())才发生变化的情况。scrollWidth
Origin
来源
Batch 180926 — bản gốc NGUYÊN VĂN nằm ở (xem mục "Bản gốc" ở trên); là bản rút gọn vanilla tự viết.
Rheinmir/uiux-asset/scroll-effects/assets/Batch 180926——原封不动的原始版本位于 (见上文“原始版本”部分); 是自行编写的精简 vanilla 版本。
Rheinmir/uiux-asset/scroll-effects/assets/