Skip to content

Biểu đồ cột kép (Tornado)

So sánh

Bên nào thắng, và thắng bao nhiêu? Neo hai giá trị đối lập vào một đường trung tâm chung và sự mất cân bằng hiện rõ chỉ trong nháy mắt - trái so với phải, nam so với nữ, trước so với sau. Đây là tháp dân số kinh điển và biểu đồ tornado, nơi cột dài nhất chính là câu chuyện.

Example
  • Nam (bên phải, màu đậm)
  • Nữ (bên trái, màu nhạt)
canvas · responsive
Tìm hiểu thêm: Hướng dẫn Insights·Hướng dẫn DevTools

Biểu đồ ở trên là cùng một engine trong mọi framework - chỉ có mã tích hợp bên dưới là khác nhau.

Khi nào nên dùng

  • Khi sự bất cân xứng chính là câu chuyện. Tháp dân số, nhập khẩu và xuất khẩu, người ủng hộ và người phản đối: hai đại lượng đối nhau trên một trục giữa, bên nào lệch là thấy ngay.
  • Báo cáo một trang cho lãnh đạo. Thanh dài nhất và bên nặng hơn truyền đạt thông điệp trước cả khi người xem kịp đọc một con số: lý tưởng khi bạn chỉ có mười giây chú ý.
  • Nếu hai giá trị không đối nghịch nhau (năm nay và năm ngoái, mục tiêu và thực tế), hãy giữ cả hai cùng một phía của trục 0 bằng biểu đồ cột so sánh.

Dữ liệu lớn trên WebGPU Thử nghiệm

DualHorizontalBarChart có tùy chọn renderer="webgpu" để vẽ các cột value1/value2 trên GPU trong khi trục, nhãn và tooltip vẫn ở lớp SVG. Tính năng này được kiểm soát theo khả năng: trên trình duyệt không có WebGPU, nó sẽ tự động hạ cấp xuống canvas, và getContext().renderer báo cáo bất kỳ renderer nào thực sự đã vẽ.

⚗️ Experimental - not yet stable. WebGPU rendering is an opt-in preview. It needs a WebGPU-capable browser (Chrome / Edge, or Safari 26+); everywhere else it falls back to canvas automatically. Axes, labels and tooltips stay on the SVG layer - only the data marks are painted on the GPU.
Heavy-data demo · ~120 rows… detecting

Xem dữ liệu chạy theo năm

Gắn date cho từng dòng rồi bật timeline: biểu đồ tornado thành câu chuyện theo từng năm với nút play và thanh tua riêng, mỗi lần hiển thị cán cân trái/phải của một giai đoạn. Mặc định tắt - không bật thì biểu đồ giữ nguyên.

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ỳ.

tsx
const ref = useRef<DualHorizontalBarChartHandle>(null);

<DualHorizontalBarChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / stepForward()
vue
<DualHorizontalBarChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" />
svelte
<div use:dualHorizontalBarChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>
ts
applyDualHorizontalBarChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });
html
<michi-vz-dual-horizontal-bar-chart id="c"></michi-vz-dual-horizontal-bar-chart>
<script>
  const el = document.getElementById("c");
  el.timeline = { speedMs: 1000, loop: true };
  // el.getTimeline() -> play() / pause() / seek(year)
</script>
  • speedMs chỉnh nhịp chạy, loop quay vòng, autoplay: true tự 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ằng tweenMseasing, hoặc đặt interpolate: 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() cho play() / pause() / toggle() / seek(period) / stepForward() / stepBack(), kèm onStepformatPeriod trong config khi cần tự dựng UI.
  • filter (top-N, sắp xếp) vẫn áp dụng bên trong từng giai đoạn, nên "top 5 mỗi năm" chạy được ngay.
  • Dòng không có date hiển thị ở mọi giai đoạn.

Cách dùng

tsx
import { DualHorizontalBarChart } from "@michi-vz/react";

export default () => <DualHorizontalBarChart {...props} />; // props = the chart options
vue
<script setup>
import { DualHorizontalBarChart } from "@michi-vz/vue";
</script>

<template>
  <DualHorizontalBarChart :options="props" />
</template>
svelte
<script>
  import { dualHorizontalBarChart } from "@michi-vz/svelte";
</script>

<div use:dualHorizontalBarChart={props}></div>
ts
// main.ts - register the elements once
import "@michi-vz/angular";
import { applyDualHorizontalBarChartProps } from "@michi-vz/angular";

// component (uses CUSTOM_ELEMENTS_SCHEMA)
// template: <michi-vz-dual-horizontal-bar-chart #c></michi-vz-dual-horizontal-bar-chart>
applyDualHorizontalBarChartProps(this.c.nativeElement, props);
html
<script type="module" src="https://cdn.jsdelivr.net/npm/@michi-vz/wc/dist/michi-vz-wc.bundle.js"></script>

<michi-vz-dual-horizontal-bar-chart id="c"></michi-vz-dual-horizontal-bar-chart>
<script>
  Object.assign(document.getElementById("c"), props); // dataSet/series, title, …
</script>
ts
import { mountDualHorizontalBarChart } from "@michi-vz/core";

const chart = mountDualHorizontalBarChart(el, props);
chart.update(next);
chart.getContext(); // renderer-agnostic, LLM-ready
chart.destroy();

API

Các prop được định kiểu là DualHorizontalBarChartProps 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 độc lập với renderer.