Rolling Number
odometer 式數字滾輪:value 變化時每一位數字獨立以 spring 垂直滾到新值,位數增減時新位淡入、舊位收攏。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
12345
0
<RollingNumber />
安裝
npx shadcn@latest add https://webberui.com/r/rolling-number.json或在 components.json 設定 registries 後,改用 @webberui/rolling-number 安裝。
安裝依賴後,從 registry JSON(/r/rolling-number.json 的 files[0].content)複製 rolling-number.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge使用
import { RollingNumber } from "@/components/ui/rolling-number";
const [price, setPrice] = React.useState(1280);
<RollingNumber value={price} prefix="$" className="text-5xl font-bold" />只要更新 value,各位數字就會自動滾到新值。字級、字重、顏色都由 className 控制,元件內部以 em 為單位計算滾輪高度,任何字級下都對齊。
帶小數與後綴:
<RollingNumber value={98.6} decimals={1} suffix="%" />Props
RollingNumber
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
value | number | — | 要顯示的數值,變化時每一位數字以滾輪滾動到新值 |
decimals | number | 0 | 小數位數 |
prefix | string | "" | 數字前綴(例如 $) |
suffix | string | "" | 數字後綴(例如 +、%) |
locale | string | "en-US" | Intl.NumberFormat locale,決定千分位與小數點符號 |
springConfig | SpringOptions | { stiffness: 300, damping: 30, mass: 0.8 } | 滾輪的 spring 參數 |
className | string | — | 附加到最外層 <span> 的 class |
細節
- 與 Animated Stats 的差異:Animated Stats 是整數 count-up——單一 MotionValue 從 0 補間到目標值、整串文字逐格重寫;Rolling Number 是 odometer 式——每一位數字是一個獨立的垂直滾輪(0–9 縱向堆疊的 column),value 變化時各滾輪分別滾到自己的目標數字。前者適合進場一次性的統計數字,後者適合會反覆變動的即時數值(價格、庫存、計數器)
- 滾輪機制:每個 digit column 高度
1em、overflow: hidden,內部 0–9 各佔1em縱向堆疊,spring 驅動translateY = -digit * 1em滾到目標格 - stagger:由低位到高位(個位先滾)每位延遲 0.03 秒,形成機械儀表的連動感
- 位數增減:字元 key 以「從右數」的位置編排,個位永遠是同一顆滾輪;位數增加時新位(與新的千分位逗號)從左側淡入並展開寬度,位數減少時舊位收攏寬度淡出(
AnimatePresence+ width 動畫) - 分隔字元:千分位逗號與小數點由
Intl.NumberFormat產生,為靜態字元不參與滾動;整體套用tabular-nums,數字等寬、滾動時不抖動
可及性
- 使用者系統開啟「減少動態效果」時,跳過滾輪與位數過渡,直接顯示新值的靜態文字
- 容器帶
aria-label(完整格式化值,含前後綴),滾輪層對輔助科技隱藏(aria-hidden),螢幕閱讀器只會朗讀當前完整數值,不會唸出滾輪裡的 0–9