Balance Scale Compare
天平比較版面:把功能籌碼勾進左右任一側,兩欄依累計權重物理下沉與翹起,支點槓桿同步傾斜並停在反映實際差距的角度。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
14
<BalanceScaleCompare />
安裝
npx shadcn@latest add https://webberui.com/r/balance-scale-compare.json或在 components.json 設定 registries 後,改用 @webberui/balance-scale-compare 安裝。
安裝依賴後,從 registry JSON(/r/balance-scale-compare.json 的 files[0].content)複製 balance-scale-compare.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion clsx tailwind-merge lucide-react調校點可選用 --wb-duration-fast、--wb-ease-out CSS 變數;未定義時會退回內建預設值。
使用
import {
BalanceScaleCompare,
type ScaleFeature,
} from "@/components/ui/balance-scale-compare";
const features: ScaleFeature[] = [
{ id: "price", label: "價格", weight: 3, icon: "💰" },
{ id: "speed", label: "效能", weight: 2, icon: "⚡" },
{ id: "support", label: "客服", weight: 2, icon: "🎧" },
];
<BalanceScaleCompare
features={features}
leftLabel="自建方案"
rightLabel="WebberUI"
defaultAssignments={{ price: "left", speed: "right" }}
/>;受控模式下傳入 assignments 並以 onAssignmentsChange 同步狀態:
const [assignments, setAssignments] = React.useState<
Record<string, "left" | "right">
>({});
<BalanceScaleCompare
features={features}
assignments={assignments}
onAssignmentsChange={setAssignments}
/>;Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
features | ScaleFeature[] | —(必填) | 可指派到任一側的功能籌碼清單 |
leftLabel | string | "方案 A" | 左側欄位標題 |
rightLabel | string | "方案 B" | 右側欄位標題 |
assignments | Record<string, "left" | "right"> | — | 受控指派表;未在表內的籌碼=尚未指派 |
defaultAssignments | Record<string, "left" | "right"> | {} | 非受控初始指派表 |
onAssignmentsChange | (next: Record<string, "left" | "right">) => void | — | 指派變更時觸發,帶入完整新指派表 |
maxTilt | number | 14 | 最大傾斜角度(度),全部權重壓在一側時的極限 |
unit | string | "分" | 權重單位標籤 |
className | string | — | 附加到最外層容器的 class |
ScaleFeature
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | —(必填) | 唯一識別 key |
label | string | —(必填) | 顯示名稱 |
weight | number | 1 | 權重(正數),加總後決定天平傾斜 |
icon | React.ReactNode | — | 可選圖示(emoji 或節點),顯示在籌碼前 |
細節
- 物理傾斜:以兩側權重差 ÷ 總權重正規化到
-1..1,再乘上maxTilt得出角度,因此天平會停在反映實際差距的角度;全部壓在一側時到達極限,兩側相等時回正 - 托盤沉降:橫桿旋轉 θ 度時,端點的垂直位移=
sin(θ) × 半桿長,兩側托盤各自跟著端點沉降或翹起,較重一側下沉、較輕一側抬起 - 圖層分離:橫桿與支點指針同屬一個旋轉組繞支點旋轉;托盤的水平定位由外層負責、垂直沉降交給內層
motion,兩者的 transform 互不干擾 - 擺盪落位:橫桿與托盤共用帶輕微過衝的 spring,指派變更後會像真實天平一樣晃幾下才停穩
- 籌碼進出:籌碼勾進某側時以 spring 彈入該側托盤、
AnimatePresence讓移出的籌碼縮小淡出,其餘籌碼以 layout 動畫補位
可及性
- 每個側別鈕是原生
button,帶aria-label(含籌碼名稱與目標側標題)與aria-pressed標示目前選取狀態,可用鍵盤操作 - 天平圖形以
aria-hidden對輔助科技隱藏,另以role="status"+aria-live="polite"的文字區朗讀目前哪一側較重與差距 - 使用者系統開啟「減少動態效果」時,橫桿與托盤改為瞬間切換、籌碼進出不縮放,功能完全不受影響