資料視覺敘事
桑基流量圖
桑基圖:節點欄位自動佈局、流帶寬度按值等比、漸層流動動畫、hover 追蹤整條路徑並淡化其他(自寫佈局,無 d3)
把「量從哪裡來、往哪裡去」畫成一張圖:流量來源 → 著陸頁 → 轉換、預算分配、能源或金流去向都適用。節點依連結拓撲自動分欄,節點高度與流帶寬度都按數值等比,流帶以來源色到目標色的漸層呈現,並帶慢速流動的虛線動畫。滑過任一條流帶時,整條上下游路徑保持高亮、其餘淡到 15%,一眼看出某個來源最後流到哪裡。佈局演算法為自寫的簡化版,不依賴 d3。
把這個效果裝進你的專案
在專案資料夾執行指令,CLI 會加入原始碼與依賴套件,再依下方使用範例引用元件。
npx webberui@latest add "https://webberui.com/r/sankey-flow.json?t=<安裝 token>"Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
360
14
12
編輯中的呼叫預設屬性
<SankeyFlow/>
完整範例包含預覽的排版與資料。先安裝元件,再把範例檔貼入你的專案。
安裝
npx webberui@latest add "https://webberui.com/r/sankey-flow.json?t=<安裝 token>"或在 components.json 設定 registries 後,改用 @webberui/sankey-flow 安裝。
使用
import {
SankeyFlow,
type SankeyLink,
type SankeyNode,
} from "@/components/ui/sankey-flow";
const nodes: SankeyNode[] = [
{ id: "search", label: "搜尋" },
{ id: "social", label: "社群" },
{ id: "home", label: "首頁" },
{ id: "campaign", label: "活動頁" },
{ id: "purchase", label: "完成購買", color: "#22c55e" },
{ id: "leave", label: "未購買", color: "#f43f5e" },
];
const links: SankeyLink[] = [
{ source: "search", target: "home", value: 420 },
{ source: "search", target: "campaign", value: 180 },
{ source: "social", target: "campaign", value: 260 },
{ source: "home", target: "purchase", value: 120 },
{ source: "home", target: "leave", value: 300 },
{ source: "campaign", target: "purchase", value: 210 },
{ source: "campaign", target: "leave", value: 230 },
];
<SankeyFlow
nodes={nodes}
links={links}
valueFormatter={(v) => `${v.toLocaleString()} 人`}
onLinkClick={(link) => console.log(link.source, "→", link.target)}
/>;Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
nodes | SankeyNode[] | — | 節點清單;id 供 links 指涉,順序決定預設配色 |
links | SankeyLink[] | — | 流量連結清單;會形成迴圈或指到不存在節點的連結將被略過並在主控台警告 |
height | number | 360 | SVG viewBox 高度(px);寬度固定為 720,實際尺寸隨容器縮放 |
nodeWidth | number | 14 | 節點矩形寬度(px) |
nodePadding | number | 12 | 同一欄節點之間的垂直間距(px) |
valueFormatter | (value: number) => string | — | 數值格式化函式,用於 tooltip、節點與螢幕閱讀器清單;未提供時以千分位顯示 |
align | "justify" | "left" | "justify" | 欄位對齊:justify 把沒有出流的終端節點推到最後一欄,left 依拓撲深度靠左排 |
onNodeClick | (node: SankeyNode) => void | — | 點擊節點(或聚焦後按 Enter/空白鍵)時回呼 |
onLinkClick | (link: SankeyLink) => void | — | 點擊流帶時回呼 |
className | string | — | 透傳到最外層容器 |
SankeyNode
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
id | string | — | 節點唯一識別碼,links 以此指涉 |
label | string | — | 顯示於節點旁的標籤文字 |
color | string | — | 節點顏色(同時決定流帶漸層端色);未提供時依 nodes 順序循環內建六色 |
SankeyLink
| 欄位 | 型別 | 預設值 | 說明 |
|---|---|---|---|
source | string | — | 來源節點 id |
target | string | — | 目標節點 id |
value | number | — | 流量值,決定流帶寬度(等比,最小 1.5px);需大於 0 |
另外具名匯出 SankeyNode、SankeyLink 與 SankeyFlowProps 型別。
細節
- 自寫的簡化佈局,不是 d3-sankey:節點欄位由連結拓撲決定(從來源方向的最長路徑),同欄依總流量遞減由上而下堆疊並垂直置中;節點高度取「流入總和」與「流出總和」較大者,全圖共用同一比例尺,數值可跨欄比較。沒有 d3-sankey 的迭代鬆弛,複雜圖可能出現較多交叉
- 流帶端點:同一節點的多條流帶依對側節點的垂直位置排序後按值切分節點高度,減少交叉;路徑為兩個控制點都在水平中點的三次貝茲 S 曲線,進出節點時保持水平
- 迴圈防呆:桑基圖必須是有向無環圖。元件依
links順序逐條檢查,若某條連結會讓圖形成迴圈(或自己連自己、指到不存在的節點、值不大於 0),就略過不畫,並在主控台console.warn提示 - hover 追蹤:滑過流帶時沿上游與下游遞迴收集整條路徑,路徑上的流帶與節點保持高亮、其餘降到 15%;滑過節點時只亮它的進出流。tooltip 顯示「來源 → 目標」與數值
- 標籤位置:左半欄的節點標籤放在節點右側、右半欄放在左側,最外兩欄的文字因此不會被裁切
可及性
- SVG 為
role="img",aria-label說明節點與流的數量;另有一份sr-only清單逐條列出「來源 流向 目標:數值」,螢幕閱讀器可完整讀到所有流量 - 每個節點都可用 Tab 聚焦,聚焦時與 hover 相同會高亮其進出流,並帶
aria-label(節點名稱與總流量)與focus-visible外框;按 Enter 或空白鍵觸發onNodeClick(IME 組字中的 Enter 會被忽略) - tooltip 為純視覺輔助(
aria-hidden),同樣的資訊已由sr-only清單提供 - 使用者系統開啟「減少動態效果」時,停用節點淡入與流帶描繪的進場動畫,也不顯示流動虛線循環,圖表直接以靜態完成狀態呈現