Horizontal Scroll Section
以縱向滾動驅動橫向位移的釘住區段,內建彈簧手感、進度條與邊緣淡出。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
16
90
26
<HorizontalScrollSection />
安裝
npx shadcn@latest add https://webberui.com/r/horizontal-scroll-section.json或在 components.json 設定 registries 後,改用 @webberui/horizontal-scroll-section 安裝。
安裝依賴後,從 registry JSON(/r/horizontal-scroll-section.json 的 files[0].content)複製 horizontal-scroll-section.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge使用
外層區段會依內容寬度自動撐出一段縱向捲動高度;使用者往下捲動時,內部軌道被釘在畫面中央並向左位移。每個直接子元素是一張橫向面板。
import { HorizontalScrollSection } from "@/components/ui/horizontal-scroll-section";
<HorizontalScrollSection gap={24} showProgress>
{items.map((item) => (
<div key={item.id} className="h-80 w-96 rounded-xl border p-6">
{item.content}
</div>
))}
</HorizontalScrollSection>;若區段位於巢狀的 overflow-y-auto 容器內,把該容器的 ref 傳給 container,元件會以它為釘住與捲動的參考:
const scrollerRef = React.useRef<HTMLDivElement>(null);
<div ref={scrollerRef} className="h-[80vh] overflow-y-auto">
<HorizontalScrollSection container={scrollerRef}>
{/* 面板 */}
</HorizontalScrollSection>
</div>;Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | React.ReactNode | — | 依序水平排列的面板,每個直接子元素為一張 |
container | React.RefObject<HTMLElement | null> | — | 巢狀捲動容器的 ref;預設以視窗為參考 |
gap | number | 16 | 面板之間的水平間距(px) |
smooth | boolean | true | 以彈簧平滑位移,帶慣性阻尼;關閉則與捲動 1:1 |
stiffness | number | 90 | 彈簧勁度(smooth 為 true 時生效) |
damping | number | 26 | 彈簧阻尼(smooth 為 true 時生效) |
showProgress | boolean | false | 在釘住區底部顯示水平進度條 |
fadeEdges | boolean | false | 左右邊緣以遮罩淡出,暗示尚有內容 |
ariaLabel | string | "橫向捲動區段" | 套用在捲動區段上的無障礙標籤 |
className | string | — | 外層區段(決定總捲動高度) |
viewportClassName | string | — | 釘住的可視視窗容器 |
trackClassName | string | — | 水平軌道 |
細節
- 自動量測:以
ResizeObserver量測軌道scrollWidth與可視視窗寬度,推導出「軌道寬 − 視窗寬」的縱向捲動距離,並據此撐出外層區段高度。內容、視窗或容器尺寸改變時會自動重新計算。 - 釘住位移:內部視窗以
sticky top-0釘住,useScroll讀取區段進度後透過useTransform映射為水平位移x。smooth開啟時再經useSpring阻尼,得到柔和的慣性收尾。 - 首幀正確:量測在 layout effect 中同步執行,第一幀即帶正確高度,避免版面跳動。所有計時器、
ResizeObserver與事件監聽於卸載時完整清理。 - 調校:
stiffness/damping控制手感,gap控制面板間距;面板的寬高由你在 children 上自訂(範例使用w-56 h-40)。
可及性
- 使用者系統開啟「減少動態效果」時,改以原生
overflow-x-auto水平捲動呈現,內容完整可及,且不佔用長段縱向捲動。 - 捲動區段帶
role="region"與可自訂的aria-label。 - 鍵盤 Tab 聚焦到目前不在視窗內的面板時,會換算成對應的縱向捲動位置,把該面板平滑帶進可視範圍,避免焦點消失在畫面外。