Data Duel Split
頁面級 A/B 對決分屏,逐回合捲動揭示指標,中央分界線像拔河般滑移、底部累計比分同步跳動。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
300
<DataDuelSplit />
安裝
npx shadcn@latest add https://webberui.com/r/data-duel-split.json或在 components.json 設定 registries 後,改用 @webberui/data-duel-split 安裝。
安裝依賴後,從 registry JSON(/r/data-duel-split.json 的 files[0].content)複製 data-duel-split.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge使用
import { DataDuelSplit } from "@/components/ui/data-duel-split";
<DataDuelSplit
left={{ name: "Nova" }}
right={{ name: "Atlas" }}
rounds={[
{ label: "首屏載入", left: 420, right: 680, suffix: "ms", win: "lower" },
{ label: "轉換率", left: 3.1, right: 4.6, suffix: "%", format: (v) => v.toFixed(1) },
{ label: "月活躍用戶", left: 128, right: 96, suffix: "k" },
]}
/>預設以整個視窗為捲動容器、舞台高度為 100vh,直接放進頁面即可成為一段捲動敘事。若要嵌在某個 overflow-y-auto 區塊裡,將該區塊的 ref 傳給 container,並把 stageHeight 設成固定像素:
const scrollerRef = React.useRef<HTMLDivElement>(null);
<div ref={scrollerRef} className="h-[300px] overflow-y-auto">
<DataDuelSplit container={scrollerRef} stageHeight="300px" left={/* … */} right={/* … */} rounds={/* … */} />
</div>Props
DataDuelSplit
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
left | DuelTeam | — | 左方陣營設定 |
right | DuelTeam | — | 右方陣營設定 |
rounds | DuelRound[] | — | 逐回合對決資料,依捲動順序揭示 |
stageHeight | string | "100vh" | 舞台高度(CSS 長度);巢狀容器內建議傳固定像素 |
container | RefObject<HTMLElement | null> | — | 巢狀 overflow 捲動容器的 ref;預設以視窗為捲動容器 |
className | string | — | 附加在最外層容器的 class |
DuelTeam
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
name | string | — | 陣營名稱 |
accent | string | 左 #4f46e5/右 #e11d48 | 強調色(任意 CSS 顏色),用於填充、比分與勝方數值 |
icon | React.ReactNode | — | 顯示在名稱旁的小圖示或標誌 |
DuelRound
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
label | string | — | 該回合對比的指標名稱 |
left | number | — | 左方數值 |
right | number | — | 右方數值 |
format | (value: number) => string | toLocaleString | 數值格式化函式 |
suffix | string | — | 數值後綴,例如 "%"、"ms"、"k" |
note | React.ReactNode | — | 該回合的補充說明 |
win | "higher" | "lower" | "higher" | 勝負判定:數值高者勝或低者勝 |
細節
- 拔河分界線:以
useScroll取得捲動進度,在各回合的優勢佔比之間連續插值,再套一層useSpring讓分界線帶點慣性,像被兩側拉扯的繩子。優勢佔比夾在 12%–88% 之間,確保兩側標籤永遠可見。 - 累計比分:比分只計入「目前回合(含)之前」的勝負,回合切換時分數以 spring 彈跳呈現「跳動」感。
win: "lower"的回合以數值較低者為勝方。 - 回合值揭示:每回合的指標數字在進場時由 0 spring 到目標值;整數維持整數、含小數則保留小數。
- 巢狀捲動:容器最外層高度為
stageHeight × 回合數,內部舞台以sticky top-0釘住。透過container支援在任意overflow-y-auto區塊內捲動。
可及性
- 使用者系統開啟「減少動態效果」時,改以靜態的逐列對比長條與最終比分呈現,不再依賴捲動與動畫。
- 視覺舞台對輔助科技隱藏(
aria-hidden),另提供一份sr-only的完整摘要清單,逐回合朗讀雙方數值與勝方。 - 最外層以
role="group"搭配aria-label標記整段對決,方便輔助科技辨識區塊用途。