Paginated Wave Grid
帶分頁器的網格版面:換頁時出場項目朝翻頁方向依距離波次退場,新頁項目自反方向湧入補位,頁碼與總數同步滾動變形。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
3
6
40
0.05
0.5
<PaginatedWaveGrid />
安裝
npx shadcn@latest add https://webberui.com/r/paginated-wave-grid.json或在 components.json 設定 registries 後,改用 @webberui/paginated-wave-grid 安裝。
安裝依賴後,從 registry JSON(/r/paginated-wave-grid.json 的 files[0].content)複製 paginated-wave-grid.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion lucide-react clsx tailwind-merge使用
import { PaginatedWaveGrid } from "@/components/ui/paginated-wave-grid";
const items = Array.from({ length: 18 }, (_, i) => ({ id: i, label: `#${i}` }));
<PaginatedWaveGrid
items={items}
pageSize={6}
columns={3}
getItemKey={(item) => item.id}
renderItem={(item) => (
<div className="flex h-24 items-center justify-center rounded-xl border">
{item.label}
</div>
)}
/>;換頁方向由前後頁碼自動判斷:往後翻時,舊頁項目依「與左緣的距離」分波次向左退場,新頁項目自右側湧入補位;往前翻則整組方向相反。底部頁碼與總數以里程表方式逐位滾動到新值。
受控模式
傳入 page 與 onPageChange 即進入受控模式,可與外部狀態或其他控制元件同步:
const [page, setPage] = React.useState(1);
<PaginatedWaveGrid page={page} onPageChange={setPage} items={items} renderItem={/* … */} />;Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
items | T[] | — | 資料陣列,依 pageSize 切成多頁 |
renderItem | (item: T, index: number) => ReactNode | — | 單一項目的渲染函式 |
pageSize | number | 6 | 每頁項目數 |
columns | number | 3 | 網格欄數 |
gap | number | 12 | 網格間距(px) |
travel | number | 40 | 波次進出場的水平位移距離(px) |
waveStagger | number | 0.05 | 每一距離階之間的延遲(秒),決定波紋速度 |
rowWeight | number | 0.5 | 列方向對波次階數的加權,>0 形成對角波紋 |
duration | number | 0.5 | 單一項目進出場時長(秒) |
blur | boolean | true | 進出場是否附帶模糊 |
loop | boolean | false | 首尾相接循環翻頁 |
page | number | — | 受控目前頁碼(1 起算) |
defaultPage | number | 1 | 非受控模式的初始頁碼 |
onPageChange | (page: number) => void | — | 頁碼變更回呼 |
showControls | boolean | true | 是否顯示內建翻頁控制列 |
getItemKey | (item: T, index: number) => Key | — | 為每個項目產生穩定 key |
aria-label | string | "分頁波次網格" | 無障礙標籤 |
className | string | — | 套用在最外層容器 |
gridClassName | string | — | 套用在網格容器 |
itemClassName | string | — | 套用在每個項目外層 |
細節
- 出場層以絕對定位疊在新頁之上(
pointer-events-none),因此新頁能立即回填版位,舊項目在其上方飛離淡出。 - 每個版位的波次階數 = 前緣欄距 + 列距 ×
rowWeight,出場與湧入共用同一階數,形成連貫的掃掠。 - 頁碼採里程表式的直向膠捲,各位數獨立滾動到目標數字;位數增減時以「右起位數」為 key,保持各欄對齊。
- 過場計時以 rAF 無關的
setTimeout收尾,於卸載與下次換頁時清除。
可及性
- 內建
上一頁/下一頁按鈕具備aria-label、鍵盤聚焦樣式,並在抵達首/末頁時停用(loop開啟時不停用)。 - 容器聚焦時支援
←/→方向鍵翻頁。 - 具
aria-live="polite"的隱藏區域,換頁時朗讀「第 X 頁,共 Y 頁」。 - 里程表數字對輔助科技隱藏(
aria-hidden),避免逐位數字造成噪音。 - 使用者系統開啟「減少動態效果」時,停用波次位移、模糊與滾動,換頁直接切換版面。