資料視覺敘事

收支平衡長條圖

以零軸為中心的雙向長條圖 React 元件:收入向上、支出向下,進場自零軸生長,淨額折線在長條長完後描繪;hover 或鍵盤聚焦顯示當期收入、支出與淨額。

把每期的收入與支出畫在同一根零軸的兩側:收入長條向上、支出長條向下,一眼看出哪幾期入不敷出。進場時長條依序自零軸生長,接著描出淨額折線;滑過任一欄或聚焦圖區後按方向鍵,該欄浮出淺色帶、其他欄淡出,tooltip 列出收入、支出與自舊值補間的淨額。預設是暗色面板,也能切到跟隨站台亮暗模式的 surface="auto"。純 SVG 自繪,不依賴圖表函式庫。

把這個效果裝進你的專案

在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。

npx webberui@latest add balance-chart

還沒有專案,或安裝遇到問題?看快速開始 →

Playground

即時調整 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型別預設值說明
dataBalanceDatum[]—每期的收入與支出,依時間順序由左而右
heightnumber280圖區高度(px);寬度固定為容器 100%
surface"dark" | "auto""dark"dark 固定暗色面板;auto 為白底並跟隨站台 dark: 模式
showNetbooleantrue是否顯示淨額點與折線
negativeTone"rose" | "neutral""rose"支出長條色調:玫瑰紅或中性灰
currencystring"NT$"金額前的貨幣前綴;提供 formatValue 時不使用
formatValue(value: number) => string—金額格式化函式,參數帶正負號;預設為貨幣加千分位整數
activeIndexnumber | null—受控的作用中期間索引;未提供時由元件內部管理
defaultActiveIndexnumber | nullnull非受控模式的初始作用中期間
onActiveIndexChange(index: number | null) => void—hover、觸控或方向鍵切換期間時回呼;Esc 清除時為 null
barGapnumber0.28每期欄寬中留白的比例(0–0.9)
titlestring"收支平衡"標題列文字
ariaLabelstring"收支平衡長條圖"圖表的無障礙名稱,也是 sr-only 資料表的標題
classNamestring—透傳到最外層容器

BalanceDatum

欄位型別預設值說明
labelstring—期間標籤,顯示在 X 軸與 tooltip
incomenumber—收入金額;負數視為 0
expensenumber—支出金額,以正數傳入,元件畫在零軸下方;負數視為 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-duration700ms每根長條的生長時長
--wb-balance-chart-stagger50ms相鄰兩期開始生長的間隔

細節

  • 值域對稱:取所有期間收入與支出的最大值,向上取整到 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 與鍵盤讀數仍可使用