資料視覺敘事
成對長條比較圖
兩組序列並排的成對長條圖:切換期間時長條以 layout 動畫重新排序與伸縮,hover 顯示兩者差距。
把兩個序列(例如今年與去年)在每個類別並排成一組長條,適合做「同期比較」。頂部是圖例與期間分段控制,切換期間時每組長條以類別名稱為 key,用 spring 動畫移到新的排序位置、高度同步伸縮;新出現的類別淡入並從 0 長高,消失的類別縮小淡出。滑過或用鍵盤移到某一組時,tooltip 顯示兩個數值與差距百分比,兩根長條頂端同時拉出一條差距括號線。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add paired-column-chartPlayground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
280
300
編輯中的呼叫預設屬性
<PairedColumnChart/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add https://webberui.com/r/paired-column-chart.json或在 components.json 設定 registries 後,改用 @webberui/paired-column-chart 安裝。
使用
import { PairedColumnChart } from "@/components/ui/paired-column-chart";
<PairedColumnChart
seriesLabels={{ a: "今年", b: "去年" }}
sortBy="a"
formatValue={(v) => `${v.toLocaleString("zh-TW")} 萬`}
onPeriodChange={(id) => console.log(`切換到 ${id}`)}
periods={[
{
id: "q1",
label: "第一季",
data: [
{ label: "信義店", a: 412, b: 380 },
{ label: "府城店", a: 298, b: 254 },
],
},
{
id: "h1",
label: "上半年",
data: [
{ label: "信義店", a: 790, b: 802 },
{ label: "府城店", a: 611, b: 520 },
],
},
]}
/>Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
periods | PairedColumnPeriod[] | — | 可切換的期間清單,每個期間各自一組類別資料 |
seriesLabels | { a: string; b: string } | — | 兩個序列的圖例名稱,也用於 tooltip 與表格標題 |
period | string | — | 受控的目前期間 id;未提供時由元件內部管理(非受控) |
defaultPeriod | string | 第一個期間 | 非受控模式的初始期間 id |
onPeriodChange | (id: string) => void | — | 切換期間時回呼 |
sortBy | "none" | "a" | "b" | "gap" | "a" | 類別排序:照資料順序、依 A 遞減、依 B 遞減、依 A 相對 B 的差距百分比遞減 |
height | number | 280 | 圖區高度(px,含 X 軸標籤) |
showValues | boolean | false | 在每根長條頂端顯示精簡格式的數值 |
formatValue | (value: number) => string | Intl.NumberFormat("zh-TW") | tooltip 與螢幕閱讀器表格的數值格式,貨幣與單位從這裡加 |
patternB | "solid" | "stripes" | "solid" | 序列 B 的填充樣式:實心或斜線 |
className | string | — | 透傳到最外層容器 |
PairedColumnPeriod
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 期間識別碼 |
label | string | — | 分段控制上顯示的名稱 |
data | PairedColumnDatum[] | — | 這個期間的各類別資料 |
PairedColumnDatum
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
label | string | — | 類別名稱;跨期間以它辨識同一組長條,請保持唯一 |
a | number | — | 序列 A 的數值(負數視為 0) |
b | number | — | 序列 B 的數值(負數視為 0) |
細節
- 圖區用 flex 加 div 長條而非 SVG rect:每組是一個
layout="position"的 motion 元素,排序改變時直接交給 layout 動畫補間位置;消失的類別經AnimatePresence的popLayout模式退場,不會卡住其他組的移動。 - Y 軸固定切成四等分,上限取「好讀的整數」(步距落在 1/2/2.5/5/10 × 10 的次方)。切換期間時格線位置不動、只換刻度數字;刻度與
showValues的數值用精簡格式(例如 1.2萬)。 - 差距百分比為 (A − B) ÷ B;B 為 0 時顯示「—」,
sortBy="gap"時這類項目排最前面。 - 節奏可用 CSS 變數覆寫:
--wb-paired-column-chart-stiffness(spring 剛性,預設 300,damping 固定 30)、--wb-paired-column-chart-duration(首次進場時長,預設 600ms)。元件每次切換期間前都會重讀這兩個變數。 - 首次進場在捲入視窗時觸發:長條從 0 長高,組與組間隔 50ms、組內 B 比 A 晚 80ms;之後的高度變化改用 spring。
- 配色:A 用
--wb-paired-column-chart-a(預設#0d9488),深色模式用--wb-paired-column-chart-a-dark(預設#2dd4bf);B 用 neutral 色階。
可及性
- 期間分段控制為
role="radiogroup",各選項role="radio"搭配aria-checked與 roving tabindex,←/→(或 ↑/↓)、Home/End 切換 - 圖區可聚焦(
role="group",aria-label為圖表摘要),聚焦後 ←/→ 移動 hover 組、Home/End 跳到頭尾、Esc 取消;目前組別的數值與差距由aria-live區域朗讀 - 另附 sr-only 的
<table>版資料(類別、兩序列數值、差距),並以aria-describedby關聯到圖區;視覺長條本身對輔助科技隱藏 - 分段控制與圖區都有 focus-visible ring
- 使用者系統開啟「減少動態效果」時,長條直接顯示最終高度、排序與期間切換立即到位,tooltip 與差距括號仍可正常使用