Recap Story Scroll
全螢幕逐幕 scroll-snap 敘事卷軸,定格時編排巨型數字與關鍵詞進場,背景色隨主題整幕切換,內建進度點與自動播放。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
4500
<RecapStoryScroll />
安裝
npx shadcn@latest add https://webberui.com/r/recap-story-scroll.json或在 components.json 設定 registries 後,改用 @webberui/recap-story-scroll 安裝。
安裝依賴後,從 registry JSON(/r/recap-story-scroll.json 的 files[0].content)複製 recap-story-scroll.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion lucide-react clsx tailwind-merge使用
以 compound components 組合:RecapStoryScroll 是 scroll-snap 容器,每個 RecapScene 為一幕,幕內用 RecapStat(巨型數字)與 RecapReveal(接力進場)編排資料。容器高度由 className 決定,各幕會填滿整個容器高度。
import {
RecapStoryScroll,
RecapScene,
RecapStat,
RecapReveal,
} from "@/components/ui/recap-story-scroll";
<RecapStoryScroll autoPlay className="h-[560px]">
<RecapScene background="#100a24" label="序章">
<RecapReveal>
<p className="text-7xl font-bold">2025</p>
</RecapReveal>
</RecapScene>
<RecapScene background="#1c0b33" label="聆聽">
<RecapStat value={48210} suffix=" 分鐘" label="總聆聽時長" />
<RecapReveal delay={0.35}>
<p className="text-sm text-white/70">相當於 33 天不間斷。</p>
</RecapReveal>
</RecapScene>
<RecapScene background="#0a2418" label="尾聲">
<RecapReveal>
<p className="text-4xl font-bold">謝謝你的 2025</p>
</RecapReveal>
</RecapScene>
</RecapStoryScroll>Props
RecapStoryScroll
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | React.ReactNode | — | 放入 RecapScene 子元件,每個為一幕 |
autoPlay | boolean | false | 掛載後是否自動逐幕播放(reduced motion 下強制停用) |
autoPlayInterval | number | 4500 | 自動播放時每幕停留時間(毫秒) |
loop | boolean | true | 播完最後一幕是否循環回第一幕 |
showProgress | boolean | true | 是否顯示右側幕別進度點 |
showKeywords | boolean | true | 是否顯示底部關鍵詞接力列(取各幕 label) |
showControls | boolean | true | 是否顯示左上幕別計數與播放控制 |
onSceneChange | (index: number) => void | — | 定格幕變更時回呼 |
className | string | — | 容器樣式;用來設定高度(預設 h-[560px]) |
RecapScene
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | React.ReactNode | — | 幕內容(RecapStat/RecapReveal 或自訂節點) |
background | string | #0a0a0a | 此幕背景色(任一 CSS 顏色值),定格時整幕背景平滑過渡至此色 |
label | string | — | 幕別標籤,用於進度點提示與底部關鍵詞接力 |
className | string | — | 附加在該幕容器上的 class |
RecapStat
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | number | — | 目標數值,定格時從 0 計數縮放至此值 |
prefix | string | "" | 數字前綴(例如 "$") |
suffix | string | "" | 數字後綴(例如 "+"、"%") |
decimals | number | 0 | 小數位數 |
locale | string | "en-US" | Intl.NumberFormat locale,決定千分位與小數點符號 |
duration | number | 1.4 | 計數動畫時長(秒) |
label | string | — | 數字下方的說明文字 |
className | string | — | 數字樣式 |
labelClassName | string | — | 說明文字樣式 |
RecapReveal
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
children | React.ReactNode | — | 進場內容(標題、關鍵詞、說明句等) |
delay | number | 0 | 定格後的進場延遲(秒),用於一幕內多段接力 |
y | number | 20 | 初始垂直位移(px) |
className | string | — | 樣式 |
細節
- 逐幕定格:容器為
snap-y snap-mandatory的捲動區,每幕高度填滿容器;以單一IntersectionObserver取最貼近視口者為定格幕。 - 資料進場:幕內元件透過 context 讀取所在幕是否定格。
RecapStat定格時從 0 計數並縮放進場、離開歸零可重播;RecapReveal以delay錯開,實作一幕之內關鍵詞逐段接力。 - 整幕換色:背景色由目前定格幕的
background驅動,於容器底層平滑過渡(backgroundColor可插值)。此元件為沉浸式深色畫布,請為各幕帶入偏深的background並在需要時以className調整文字顏色。 - 自動播放:
autoPlay於掛載後啟動、每autoPlayInterval前進一幕;使用者以滾輪或觸控互動時自動暫停,也可用左上按鈕手動切換。 - 關鍵詞接力:底部依序列出各幕
label,目前定格幕的關鍵詞點亮,其餘淡出,形成交棒式敘事索引。
可及性
- 使用者系統開啟「減少動態效果」時,保留 scroll-snap 版面與內容(全部直接顯示),停用計數/縮放進場、整幕換色過渡與自動播放,跳幕改為即時捲動。
- 捲動區可聚焦,支援方向鍵/PageUp/PageDown/Home/End 逐幕導覽;任一鍵盤或滑鼠互動都會暫停自動播放。
- 右側進度點為原生按鈕,可鍵盤聚焦與點擊跳幕,定格幕以
aria-current標記;播放鈕帶aria-pressed與aria-label。 - 每幕帶
aria-roledescription="幕"與aria-label;RecapStat計數層對輔助科技隱藏,並以aria-label朗讀最終值。