G02▸S102▸gsap
Horizontal Scroll
直滾變橫向
難度 ★★★★★標籤 scrollTrigger · pin · horizontal3 min read
效果簡述
頁面被釘住,垂直滾動轉成水平移動 track。作品集 / 案例展示愛用。 注意:很驚艷但破壞使用者預期,務必告知客戶後果。
↓ 直滾變橫向 · scrub: 0.5
案例 01
案例 02
案例 03
案例 04
案例 05
Claude Code Prompt
參考:S102 horizontal scroll
技術:gsap + ScrollTrigger pin + x 位移
具體行為:
- 父 wrap 高度 = viewport,內含 track(flex-row, w-max)
- distance = track.scrollWidth - wrap.clientWidth
- gsap.to(track, { x: -distance, ease: 'none', scrollTrigger: { trigger: wrap, start: 'top top', end: '+=' + distance, pin: true, scrub: 0.5 } })
限制:
- mobile 體驗差,建議改成原生橫向滾(overflow-x-auto)
- 別在第一個 viewport 就用(使用者還沒準備好)
- 結束後要留正常滾動空間,不然「卡關」感很強完整程式碼
'use client'
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import { useEffect, useRef } from 'react'
export function HorizontalScroll() {
const wrap = useRef<HTMLDivElement>(null)
const track = useRef<HTMLDivElement>(null)
useEffect(() => {
gsap.registerPlugin(ScrollTrigger)
const ctx = gsap.context(() => {
if (!wrap.current || !track.current) return
const distance = track.current.scrollWidth - wrap.current.clientWidth
gsap.to(track.current, {
x: -distance,
ease: 'none',
scrollTrigger: {
trigger: wrap.current,
start: 'top top',
end: '+=' + distance,
pin: true,
scrub: 0.5,
},
})
}, wrap)
return () => ctx.revert()
}, [])
return (
<section ref={wrap} className="h-screen overflow-hidden">
<div ref={track} className="flex h-full w-max gap-6 px-6">
{/* 案例卡 */}
</div>
</section>
)
}何時用
- ✅ 作品集案例 gallery
- ✅ 時間軸 / process 展示
- ✅ 故事性強的 walkthrough
何時別用
- ❌ Landing page 第一屏
- ❌ 主要受眾用手機(直接給 overflow-x-auto 比較好)
- ❌ 內容需要被搜尋 / 索引(橫向不友善)
我踩過的坑
-
wrap 要 overflow-hidden:否則 track 滿出來會破版。
-
distance 算錯動畫提前結束:scrollWidth - clientWidth 才是 「track 要走的距離」。寫成 scrollWidth 會多走一個視窗寬。
-
手機改用原生橫滾:detect
matchMedia('(pointer: coarse)')時跳過 GSAP,直接overflow-x-auto。
相關效果
- G01 Pin Section + Scrub — 同機制