資料視覺敘事
收支平衡長條圖
以零軸為中心的雙向長條圖 React 元件:收入向上、支出向下,進場自零軸生長,淨額折線在長條長完後描繪;hover 或鍵盤聚焦顯示當期收入、支出與淨額。
把每期的收入與支出畫在同一根零軸的兩側:收入長條向上、支出長條向下,一眼看出哪幾期入不敷出。進場時長條依序自零軸生長,接著描出淨額折線;滑過任一欄或聚焦圖區後按方向鍵,該欄浮出淺色帶、其他欄淡出,tooltip 列出收入、支出與自舊值補間的淨額。預設是暗色面板,也能切到跟隨站台亮暗模式的 surface="auto"。純 SVG 自繪,不依賴圖表函式庫。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add balance-chartPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
280
700
50
編輯中的呼叫預設屬性
<BalanceChart/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add https://webberui.com/r/balance-chart.json或在 components.json 設定 registries 後,改用 @webberui/balance-chart 安裝。
使用
import { BalanceChart } from "@/components/ui/balance-chart";
const data = [
{ label: "1月", income: 168000, expense: 142500 },
{ label: "2月", income: 121000, expense: 133800 },
{ label: "3月", income: 152400, expense: 139600 },
];
<BalanceChart
title="本季收支"
data={data}
currency="NT$"
onActiveIndexChange={(i) => console.log(i === null ? "清除" : data[i].label)}
/>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
data | BalanceDatum[] | — | 每期的收入與支出,依時間順序由左而右 |
height | number | 280 | 圖區高度(px);寬度固定為容器 100% |
surface | "dark" | "auto" | "dark" | dark 固定暗色面板;auto 為白底並跟隨站台 dark: 模式 |
showNet | boolean | true | 是否顯示淨額點與折線 |
negativeTone | "rose" | "neutral" | "rose" | 支出長條色調:玫瑰紅或中性灰 |
currency | string | "NT$" | 金額前的貨幣前綴;提供 formatValue 時不使用 |
formatValue | (value: number) => string | — | 金額格式化函式,參數帶正負號;預設為貨幣加千分位整數 |
activeIndex | number | null | — | 受控的作用中期間索引;未提供時由元件內部管理 |
defaultActiveIndex | number | null | null | 非受控模式的初始作用中期間 |
onActiveIndexChange | (index: number | null) => void | — | hover、觸控或方向鍵切換期間時回呼;Esc 清除時為 null |
barGap | number | 0.28 | 每期欄寬中留白的比例(0–0.9) |
title | string | "收支平衡" | 標題列文字 |
ariaLabel | string | "收支平衡長條圖" | 圖表的無障礙名稱,也是 sr-only 資料表的標題 |
className | string | — | 透傳到最外層容器 |
BalanceDatum
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
label | string | — | 期間標籤,顯示在 X 軸與 tooltip |
income | number | — | 收入金額;負數視為 0 |
expense | number | — | 支出金額,以正數傳入,元件畫在零軸下方;負數視為 0 |
CSS 變數
| 變數 | 預設值 | 說明 |
|---|---|---|
--wb-balance-chart-positive | #2dd4bf(auto 亮色為 #0d9488) | 收入長條顏色 |
--wb-balance-chart-negative | #fb7185(auto 亮色為 #f43f5e) | negativeTone="rose" 時的支出長條顏色,也用於負的淨額數字 |
--wb-balance-chart-accent | #2dd4bf(auto 亮色為 #0d9488) | 正的淨額數字顏色 |
--wb-balance-chart-duration | 700ms | 每根長條的生長時長 |
--wb-balance-chart-stagger | 50ms | 相鄰兩期開始生長的間隔 |
細節
- 值域對稱:取所有期間收入與支出的最大值,向上取整到 1、2、2.5、5、10 的倍數,上下各兩段格線;刻度數字以
Intl.NumberFormat("zh-TW")的精簡寫法顯示(例如「10萬」)。 - 長條以
scaleY自零軸生長(收入以底邊為原點、支出以頂邊為原點),緩動cubic-bezier(0.22,1,0.36,1);淨額折線以pathLength在所有長條長完後描繪 600ms,淨額點沿線依序浮現。 - 進場由
useInView在圖表進入視窗(margin-10% 0px)時觸發一次;寬度以ResizeObserver量測,SSR 時使用後備寬度 560px,掛載後自動對齊容器。 - 作用中欄的淺色帶與 tooltip 以 spring(stiffness 400、damping 32)在欄與欄之間滑動;tooltip 擺在該欄的另一側並夾在容器內,不會蓋住長條或溢出。
- 欄寬放不下 X 標籤時自動隔幾個顯示一次,作用中的那期一律顯示並加粗。
可及性
- 圖區是可聚焦的
role="group",按 ← → 切換期間、Home/End 跳到首尾、Esc 清除;聚焦時顯示 focus-visible 外框 svg為role="img",aria-label摘要期數、淨額合計與最高/最低的期間- 另附 sr-only 的
<table>版資料(期間、收入、支出、淨額),並以aria-describedby關聯到圖區 - 切換期間時,sr-only 的
aria-live="polite"區域朗讀該期收入、支出與淨額;視覺 tooltip 對輔助科技隱藏以免重複 - 使用者系統開啟「減少動態效果」時,長條生長與折線描繪直接顯示終態、色帶與 tooltip 不滑動、淨額不補間;hover 與鍵盤讀數仍可使用