Sankey
Dòng chảy
"Tất cả rồi sẽ đi đâu?" Một biểu đồ Sankey theo dõi dòng chảy qua một hệ thống: các nút được bố trí theo cột, và độ dày của mỗi dải chính là giá trị dòng chảy. Đây là hình ảnh phù hợp cho thương mại giữa các nhà xuất khẩu và thị trường, ngân sách từ nguồn đến nơi sử dụng, lưu lượng truy cập từ nguồn giới thiệu đến các trang - bất cứ nơi nào một đại lượng tách ra rồi hợp lại trên đường đi của nó.
Bố cục được tính toán bằng d3-sankey: các nút được gán vào các cột từ cấu trúc topology của đồ thị, sắp xếp theo chiều dọc, và các liên kết được vẽ thành các dải ngang mượt. Di chuột vào một nút hoặc một dòng chảy để xem con số.
Khi nào nên dùng
- Dòng chảy có cấu trúc. Thương mại giữa nước xuất khẩu và thị trường, ngân sách từ nguồn đến khoản chi, người dùng đi qua phễu: lượng đến từ đâu, đi về đâu, tách nhập ra sao trên đường đi.
- Tìm tuyến chủ đạo và chỗ rò rỉ. Độ dày của dải chính là giá trị, nên tuyến lớn và những thất thoát nhỏ đều hiện rõ tức thì: góc nhìn kiểm toán cho mọi hệ thống.
- Chỉ có một chặng? Nếu không có gì chảy xuyên qua, biểu đồ cột so sánh xếp hạng các nguồn gọn gàng hơn một Sankey hai cột.
Dữ liệu lớn trên WebGPU Thử nghiệm
renderer="webgpu" tùy chọn của biểu đồ này vẽ các mark trên GPU trong khi trục/nhãn/tooltip vẫn nằm trên SVG; được kiểm soát theo khả năng phần cứng với tự động chuyển về canvas.
Xem dữ liệu chạy theo năm
Gắn date cho từng liên kết rồi bật timeline: snapshot của một năm là các liên kết chia sẻ date đó - các nút dùng chung qua mọi năm, chỉ có dòng chảy đến rồi đi - và độ dày dải tự biến hình mượt giữa các năm. Mặc định tắt - không bật thì biểu đồ giữ nguyên. Đây là kiểu chạy từng năm có tương tác, không phải hiệu ứng vào cảnh một lần bên dưới.
Bấm nút play dưới biểu đồ: dữ liệu chạy qua từng năm, mỗi lần một snapshot. Kéo thanh tua để nhảy đến năm bất kỳ.
const ref = useRef<SankeyChartHandle>(null);
<SankeyChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / stepForward()<SankeyChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" /><div use:sankeyChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>applySankeyChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });<michi-vz-sankey-chart id="c"></michi-vz-sankey-chart>
<script>
const el = document.getElementById("c");
el.timeline = { speedMs: 1000, loop: true };
// el.getTimeline() -> play() / pause() / seek(year)
</script>speedMschỉnh nhịp chạy,loopquay vòng,autoplay: truetự chạy khi mount,showControl: falseẩn thanh điều khiển có sẵn.- Giá trị trượt mượt giữa các giai đoạn theo mặc định (
interpolate); chỉnh chuyển động bằngtweenMsvàeasing, hoặc đặtinterpolate: falseđể cắt thẳng. Khi bật reduced motion, biểu đồ luôn cắt thẳng. - Controller headless luôn sẵn sàng:
chart.timeline()choplay() / pause() / toggle() / seek(period) / stepForward() / stepBack(), kèmonStepvàformatPeriodtrong config khi cần tự dựng UI. - Liên kết không có
datevẫn hiển thị ở mọi giai đoạn. timelinethắngprogressiveDrawkhi cả hai cùng đặt - hiệu ứng vẽ dần bên dưới đứng yên khi timeline đang điều khiển.
Hiệu ứng vẽ dần
Biểu đồ tự vẽ dần từ trái sang phải ngay khi mount, hiện lần lượt các nét vẽ trước khi ổn định vào vị trí. Mặc định tắt - không bật thì biểu đồ giữ nguyên như cũ.
Mỗi đường lớn dần từ năm đầu đến năm cuối; nhãn bám theo ngọn đường rồi dừng ở điểm cuối. Khi hệ điều hành bật reduced motion, biểu đồ hiển thị đầy đủ ngay lập tức.
progressiveDraw: true dùng cấu hình mặc định (1200 ms, easeInOutCubic). Truyền object để tinh chỉnh:
const ref = useRef<SankeyChartHandle>(null);
<SankeyChart
ref={ref}
{...props}
progressiveDraw={{ durationMs: 2000 }}
/>;
// ref.current?.replay() chạy lại hiệu ứng khi cần<SankeyChart :options="{ ...props, progressiveDraw: { durationMs: 2000 } }" /><div use:sankeyChart={{ ...props, progressiveDraw: { durationMs: 2000 } }}></div>applySankeyChartProps(this.c.nativeElement, {
...props,
progressiveDraw: { durationMs: 2000 },
});<michi-vz-sankey-chart id="c"></michi-vz-sankey-chart>
<script>
const el = document.getElementById("c");
el.progressiveDraw = { durationMs: 2000 };
// el.replay() chạy lại hiệu ứng
</script>durationMsvàeasing("linear", "easeOutQuad", "easeInOutCubic", hoặc hàm(t) => ttự viết) quyết định nhịp vẽ.autoplay: falsehiển thị biểu đồ vẽ sẵn đầy đủ; gọireplay()(ref handle bên React, method của web component, hoặc instance của core) để chạy hiệu ứng khi cần.replayOnUpdate: truechạy lại mỗi lần dữ liệu thay đổi.- Tôn trọng
prefers-reduced-motion: biểu đồ hiển thị đầy đủ ngay, không animation. - Hiệu ứng vẽ dần chỉ chạy một lần khi mount; mục xem dữ liệu chạy theo năm ở trên chạy từng năm một theo yêu cầu.
Cách dùng
import { SankeyChart } from "@michi-vz/react";
export default () => <SankeyChart {...props} />; // props = the chart options<script setup>
import { SankeyChart } from "@michi-vz/vue";
</script>
<template>
<SankeyChart :options="props" />
</template><script>
import { sankeyChart } from "@michi-vz/svelte";
</script>
<div use:sankeyChart={props}></div>// main.ts - register the elements once
import "@michi-vz/angular";
import { applySankeyChartProps } from "@michi-vz/angular";
// component (uses CUSTOM_ELEMENTS_SCHEMA)
// template: <michi-vz-sankey-chart #c></michi-vz-sankey-chart>
applySankeyChartProps(this.c.nativeElement, props);<script type="module" src="https://cdn.jsdelivr.net/npm/@michi-vz/wc/dist/michi-vz-wc.bundle.js"></script>
<michi-vz-sankey-chart id="c"></michi-vz-sankey-chart>
<script>
Object.assign(document.getElementById("c"), props); // nodes, links, …
</script>import { mountSankeyChart } from "@michi-vz/core";
const chart = mountSankeyChart(el, props);
chart.update(next);
chart.getContext(); // renderer-agnostic, LLM-ready
chart.destroy();Cấu trúc dữ liệu
Khác với các biểu đồ khác, một Sankey nhận hai mảng: nodes (mỗi nút có một id duy nhất, một label và color tùy chọn) và links (source → target theo id, kèm một value).
const props = {
linkColorMode: "source", // colour links by their source (or "target")
nodes: [
{ id: "France" }, { id: "Germany" },
{ id: "EU" }, { id: "Asia" },
],
links: [
{ source: "France", target: "EU", value: 40 },
{ source: "France", target: "Asia", value: 22 },
{ source: "Germany", target: "EU", value: 55 },
{ source: "Germany", target: "Asia", value: 35 },
],
};Một liên kết trỏ tới một id nút không xác định (hoặc một nút nằm trong disabledItems) sẽ bị bỏ kèm một datawarning; vô hiệu hóa một nút cũng bỏ luôn các liên kết của nó.
Các tùy chỉnh bố cục
nodeWidth đặt chiều rộng hình chữ nhật của nút, nodePadding khoảng cách dọc giữa các nút trong một cột, và linkOpacity độ trong suốt của các dải. linkColorMode tô màu mỗi dải theo nút source (mặc định) hoặc target của nó. Bản sao a11y và getContext() hiển thị các liên kết dưới dạng bảng "Nguồn → Đích: giá trị" có thể đọc được.
Nút bo tròn. nodeRadius (px, mặc định 2) bo tròn các góc hình chữ nhật của nút - tăng lên để có kiểu viên thuốc, hoặc đặt 0 cho góc vuông. Giá trị này bị giới hạn ở nửa cạnh ngắn hơn của nút, nên không bao giờ làm biến dạng một nút mỏng.
Dòng chảy bo tròn. Các dòng chảy được vẽ dưới dạng dải tô đặc; linkRadius (px, mặc định 2) bo tròn các góc của chúng nơi gặp các nút, tạo kết nối mềm mại hơn (bị giới hạn ở nửa độ dày của dải; 0 = sắc cạnh). linkColorMode tô màu mỗi dòng chảy theo nút source hoặc target của nó, ở mức linkOpacity:
const props = { nodeRadius: 4, linkRadius: 4, /* …nodes, links */ };API
Các prop được định kiểu là SankeyChartProps trong @michi-vz/core. Dùng chung cho mọi biểu đồ: width, height, margin, colors / colorsMapping, renderer ("svg", "canvas", hoặc "webgpu" thử nghiệm), highlightItems, disabledItems, và các callback on*. onChartDataProcessed / getContext() trả về ChartContext không phụ thuộc bộ dựng. Tham chiếu đầy đủ: API Sankey.
