gsap-scrolltrigger-pin

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Skill: 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
    scroll-effects
    ) hay làm mượt cảm giác cuộn toàn trang (dùng
    lenis-smooth-scroll
    ) — hai skill đó KHÔNG kéo theo dependency GSAP. Chỉ áp dụng cho MỘT section pin cụ thể.
  • 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
    ),也不用于让整页滚动更顺滑(使用
    lenis-smooth-scroll
    )——这两个 skill 均不会引入 GSAP dependency。此 skill 只适用于一个特定的待 pin section。

Mental model

心智模型

[data-hp-section] > .hp-pin (pin) > [data-hp-track] (tween xPercent, scrub) > [data-hp-slide] × N
;
end
là hàm đo lại
track.scrollWidth
mỗi
refresh()
;
matchMedia
chọn nhánh GSAP hoặc nhánh reduce-motion (CSS cuộn ngang thường).
[data-hp-section] > .hp-pin (pin) > [data-hp-track] (tween xPercent, scrub) > [data-hp-slide] × N
;
end
是一个函数,每次
refresh()
时都会重新测量
track.scrollWidth
;
matchMedia
用于选择 GSAP 分支或 reduce-motion 分支(使用常规 CSS 横向滚动)。

Input và output contract

输入与输出约定

FieldRequired?Ý nghĩa
Innội dung slide của sectioncócase study / portfolio / sản phẩm
Inquyền kéo lib ngoài (GSAP)cókhông được kéo lib → dùng bản rút gọn
assets/
, GSAP vẫn bắt buộc
In
design.md
của dự án
khôngcó thì đổi token màu theo nó
Outsection pin + track ngang trong dự áncócopy nguyên file gốc (giữ comment ghi công) hoặc adapt
assets/
Outbằng chứng verify
playwright-verify
cópin đúng lúc, không overflow ngang, sống sót resize
字段必填?含义
输入section 的 slide 内容是case study / portfolio / 产品
输入引入外部库(GSAP)的权限是不允许引入 lib → 使用精简版
assets/
,但 GSAP 仍是必需依赖
输入项目的
design.md
否如果存在,可根据它调整颜色 token
输出项目中的 pinned section + 横向 track是复制完整的原始文件(保留署名 comment),或适配
assets/
输出
playwright-verify
验证证据
是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>
      +
      .../ScrollTrigger.min.js
      , load TRƯỚC script của skill.
    • npm:
      npm install gsap
      , rồi
      import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger);
      .
  • RULE-02 (MUST):
    prefers-reduced-motion: reduce
    → nhánh GSAP KHÔNG init gì cả (không ScrollTrigger, không pin, không scrub) — CSS tự chuyển
    .hp-pin
    thành
    overflow-x: auto
    cuộn ngang thường (
    scroll-snap-type: x mandatory
    ). Đừng thêm animation JS nào trong nhánh này.
  • RULE-03 (MUST): Track dùng
    xPercent
    , không dùng
    x
    (px) —
    xPercent
    tự 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.
  • RULE-04 (MUST): Verify bằng
    playwright-verify
    : 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
    ScrollTrigger.refresh()
    đã bắt layout mới.
  • Capabilities: đọc bản gốc (web/git) + asset skill +
    design.md
    ; ghi HTML/CSS/JS vào dự án đích; chạy trình duyệt headless để verify.
  • 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>
      +
      .../ScrollTrigger.min.js
      ,必须在 skill 脚本之前加载。
    • npm:执行
      npm install gsap
      ,然后运行
      import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger);
      。
  • RULE-02(MUST):当
    prefers-reduced-motion: reduce
    时,GSAP 分支不进行任何初始化(无 ScrollTrigger、无 pin、无 scrub)——CSS 会自动将
    .hp-pin
    转换为带
    overflow-x: auto
    的常规横向滚动(
    scroll-snap-type: x mandatory
    )。不要在此分支中添加任何 JS 动画。
  • RULE-03(MUST):Track 必须使用
    xPercent
    ,不能使用
    x
    (px)——
    xPercent
    会自动跟随 track 的实际宽度,即使 slide 内容尺寸发生变化,也无需手动重新计算 px offset。
  • RULE-04(MUST):必须使用
    playwright-verify
    验证:纵向滚过整个 section,断言 section 在恰当时机被 pin(当 track 尚未移动完所有 slide 时,viewport 不得继续纵向滚动);断言页面剩余部分不存在 horizontal overflow;测试 resize viewport 后再次滚动,以确认
    ScrollTrigger.refresh()
    已获取新的 layout。
  • 能力:读取原始版本(web/git)+ skill asset +
    design.md
    ;将 HTML/CSS/JS 写入目标项目;运行 headless browser 进行验证。

Failure boundaries

失败边界

  • Thiếu GSAP + ScrollTrigger và không được thêm dependency → blocked (RULE-01), đề xuất
    scroll-effects
    /
    lenis-smooth-scroll
    nếu yêu cầu thật ra không cần pin.
  • Mở file bằng
    file://
    → hành vi không đúng; phải chạy local qua HTTP.
  • 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
    。
  • 通过
    file://
    打开文件 → 行为不正确;必须通过 HTTP 运行本地服务。
  • 验证断言失败(未 pin、出现 horizontal overflow、resize 后布局损坏)→ failed;修复后重新验证。

HOW

HOW(流程)

Main workflow

主流程

StepTypeInputsActionOutputs/exitFailure/next
W01judgmentyêu cầuXác nhận cần pin + cuộn ngang (không phải
scroll-effects
/
lenis-smooth-scroll
)
chọn skillkhông cần pin → chuyển skill khác
W02effectbảng Pen gốcMở link chạy, copy nguyên file
.html
(+
_assets/
) vào dự án, giữ comment ghi công
file gốc trong dự ánkhông được kéo lib ngoài → B01
W03effectfile +
design.md
Chỉ sửa nội dung/ảnh/màusection đã adapt—
W04deterministictrang chạy qua HTTPVerify RULE-04 bằng
playwright-verify
assert xanhđỏ → sửa, lặp W04
StepTypeInputsActionOutputs/exitFailure/next
W01judgment用户需求确认需要 pin + 横向滚动(而非
scroll-effects
/
lenis-smooth-scroll
)
选择 skill不需要 pin → 切换到其他 skill
W02effect原始 Pen打开可运行的链接,将完整的
.html
文件(以及
_assets/
)复制到项目中,并保留署名 comment
项目中包含原始文件不允许引入外部 lib → B01
W03effect文件 +
design.md
仅修改内容、图片和颜色已适配的 section—
W04deterministic通过 HTTP 运行的项目页面使用
playwright-verify
验证 RULE-04
断言通过失败 → 修复并重复 W04

Branches

分支

IDKindGuardHành viSkip / failureRejoin
B01conditional_requireddự án KHÔNG được kéo lib ngoài (bản gốc)dùng bản rút gọn vanilla
assets/
(mục Reference — Assets), đọc rồi ADAPT
vẫn cần GSAP (RULE-01)W04
B02conditional_required
prefers-reduced-motion: reduce
không init GSAP, CSS cuộn ngang thường (RULE-02)—W04
IDKindGuard行为Skip / failureRejoin
B01conditional_required项目不允许引入外部 lib(原始版本)使用自行编写的精简 vanilla
assets/
(见 Reference — Assets),读取后进行 ADAPT
仍然需要 GSAP(RULE-01)W04
B02conditional_required
prefers-reduced-motion: reduce
不初始化 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
    horizontal-scroll-section-with-gsap-and-locomotive-scroll.html
    từ
    Rheinmir/uiux-asset
    , đổ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.
  • Boundary/failure: dùng
    x: -2000
    (px) thay
    xPercent
    → đổi kích thước viewport thì track lệch/hụt slide cuối → vi phạm RULE-03, đổi sang
    xPercent: -100 * (slideCount - 1)
    rồi verify resize lại.
  • Positive:“制作一个 portfolio 横向滚动 section”→ 从
    Rheinmir/uiux-asset
    复制
    horizontal-scroll-section-with-gsap-and-locomotive-scroll.html
    ,替换图片和文字 → 使用 playwright 纵向滚动:section 被 pin,track 滑完所有 slide 后才释放,且不存在 horizontal overflow。
  • **边界/失败:**使用
    x: -2000
    (px)代替
    xPercent
    → 改变 viewport 尺寸后,track 会偏移或漏掉最后一个 slide → 违反 RULE-03;改用
    xPercent: -100 * (slideCount - 1)
    ,然后重新执行 resize 验证。

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
Rheinmir/uiux-asset
— 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
.html
(+ thư mục
_assets/
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
file://
.
git clone --depth 1 https://github.com/Rheinmir/uiux-asset
nếu cần cả bộ offline.
Pen gốc — tác giảLink
Horizontal Scroll Section with GSAP and Locomotive Scroll — cameronknightchạy · code
Smooth Scroll Stacking Accordion — supahchạy · code
Infinite Horizontal Scroll with Progress Tracking — haptichashchạy · code
Pinned Split-Screen Mask Reveal — gridmorphicchạy · code
Scroll-Driven SVG Map Editorial Gallery — knyttnevechạy · code
assets/
trong skill này chỉ là bản rút gọn vanilla tự viết — dùng khi dự án KHÔNG được kéo lib ngoài; mặc định dùng bản gốc ở trên.
作者提供的原封不动的原始代码(CodePen public = MIT)位于外部仓库
Rheinmir/uiux-asset
——可直接运行的 gallery:https://rheinmir.github.io/uiux-asset/scroll-effects/ 。 流程:打开可运行的链接以查看准确效果 → 将完整的
.html
文件(若文件引用了
_assets/
,则一并复制)复制到项目 → 保留首行署名 comment → 仅修改内容、图片和颜色。通过 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 版本——当项目不允许引入外部 lib 时使用;默认使用上述原始版本。

Reference — Assets

Reference — Assets

assets/demo.html
+
assets/gsap-scrolltrigger-pin.css
+
assets/gsap-scrolltrigger-pin.js
— đọc rồi ADAPT (đổi nội dung slide, token màu theo
design.md
của dự án nếu có). Mở
demo.html
trực tiếp bằng trình duyệt để xem cơ chế chạy thật (asset load GSAP qua CDN).
assets/demo.html
+
assets/gsap-scrolltrigger-pin.css
+
assets/gsap-scrolltrigger-pin.js
——读取后进行 ADAPT(修改 slide 内容;如果项目存在
design.md
,则按其调整颜色 token)。直接在浏览器中打开
demo.html
,即可查看真实的运行机制(asset 通过 CDN 加载 GSAP)。

Reference — Cơ chế

Reference — 实现机制

  • Markup:
    [data-hp-section] > .hp-pin > [data-hp-track] > [data-hp-slide] × N
    — track là flex-row, mỗi slide
    flex: 0 0 100vw
    , tổng width = N × 100vw (tự nhiên theo layout, không tính tay).
  • ScrollTrigger
    pin
    .hp-pin
    (
    pin: true
    ,
    start: "top top"
    ),
    scrub: 1
    khoá tiến trình animation vào đúng vị trí cuộn — không phải animation chạy theo thời gian.
  • Animation là
    gsap.to(track, { xPercent: -100 * (slideCount - 1) })
    —
    slideCount
    đọc từ
    track.querySelectorAll('[data-hp-slide]').length
    , không hardcode số slide trong code.
  • end
    là một HÀM:
    () => "+=" + (track.scrollWidth - window.innerWidth)
    — tính lại mỗi lần
    ScrollTrigger.refresh()
    chạy (đo lại theo
    track.scrollWidth
    thật của DOM tại thời điểm đó), không phải một số cố định.
  • ScrollTrigger.matchMedia()
    bọc toàn bộ init: khi viewport đổi breakpoint hoặc
    prefers-reduced-motion
    đổ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.
  • window.addEventListener('load', () => ScrollTrigger.refresh())
    bắt trường hợp ảnh/font load xong mới đổi
    scrollWidth
    thật của track.
  • Markup:
    [data-hp-section] > .hp-pin > [data-hp-track] > [data-hp-slide] × N
    ——track 使用
    flex-row
    ,每个 slide 设置
    flex: 0 0 100vw
    ,总 width = N × 100vw(自然跟随 layout,无需手动计算)。
  • ScrollTrigger
    会 pin
    .hp-pin
    (
    pin: true
    、
    start: "top top"
    ),而
    scrub: 1
    会将动画进度锁定到准确的滚动位置——它不是按时间运行的动画。
  • 动画为
    gsap.to(track, { xPercent: -100 * (slideCount - 1) })
    ——
    slideCount
    从
    track.querySelectorAll('[data-hp-slide]').length
    读取,不能在代码中 hardcode slide 数量。
  • end
    是一个函数:
    () => "+=" + (track.scrollWidth - window.innerWidth)
    ——每次
    ScrollTrigger.refresh()
    运行时都会重新计算(在当时的 DOM 中测量真实的
    track.scrollWidth
    ),而不是使用固定数值。
  • ScrollTrigger.matchMedia()
    包裹整个初始化过程:当 viewport 跨越 breakpoint 或
    prefers-reduced-motion
    发生变化时,GSAP 会自动 revert 旧分支并运行匹配的新分支——这是通过 resize 保持功能正常的标准方式,无需手动编写 resize listener。
  • window.addEventListener('load', () => ScrollTrigger.refresh())
    用于处理图片或字体加载完成后,track 的实际
    scrollWidth
    才发生变化的情况。

Origin

来源

Batch 180926 — bản gốc NGUYÊN VĂN nằm ở
Rheinmir/uiux-asset/scroll-effects/
(xem mục "Bản gốc" ở trên);
assets/
là bản rút gọn vanilla tự viết.
Batch 180926——原封不动的原始版本位于
Rheinmir/uiux-asset/scroll-effects/
(见上文“原始版本”部分);
assets/
是自行编写的精简 vanilla 版本。