資料視覺敘事

成對長條比較圖

兩組序列並排的成對長條圖:切換期間時長條以 layout 動畫重新排序與伸縮,hover 顯示兩者差距。

把兩個序列(例如今年與去年)在每個類別並排成一組長條,適合做「同期比較」。頂部是圖例與期間分段控制,切換期間時每組長條以類別名稱為 key,用 spring 動畫移到新的排序位置、高度同步伸縮;新出現的類別淡入並從 0 長高,消失的類別縮小淡出。滑過或用鍵盤移到某一組時,tooltip 顯示兩個數值與差距百分比,兩根長條頂端同時拉出一條差距括號線。

把這個效果裝進你的專案

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

npx webberui@latest add paired-column-chart

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

Playground

即時調整 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型別預設值說明
periodsPairedColumnPeriod[]—可切換的期間清單,每個期間各自一組類別資料
seriesLabels{ a: string; b: string }—兩個序列的圖例名稱,也用於 tooltip 與表格標題
periodstring—受控的目前期間 id;未提供時由元件內部管理(非受控)
defaultPeriodstring第一個期間非受控模式的初始期間 id
onPeriodChange(id: string) => void—切換期間時回呼
sortBy"none" | "a" | "b" | "gap""a"類別排序:照資料順序、依 A 遞減、依 B 遞減、依 A 相對 B 的差距百分比遞減
heightnumber280圖區高度(px,含 X 軸標籤)
showValuesbooleanfalse在每根長條頂端顯示精簡格式的數值
formatValue(value: number) => stringIntl.NumberFormat("zh-TW")tooltip 與螢幕閱讀器表格的數值格式,貨幣與單位從這裡加
patternB"solid" | "stripes""solid"序列 B 的填充樣式:實心或斜線
classNamestring—透傳到最外層容器

PairedColumnPeriod

欄位型別預設值說明
idstring—期間識別碼
labelstring—分段控制上顯示的名稱
dataPairedColumnDatum[]—這個期間的各類別資料

PairedColumnDatum

欄位型別預設值說明
labelstring—類別名稱;跨期間以它辨識同一組長條,請保持唯一
anumber—序列 A 的數值(負數視為 0)
bnumber—序列 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 與差距括號仍可正常使用