Skip to content

Biểu đồ vùng ​

Cấu thành

Tổng đang tăng, nhưng lát nào đang thúc đẩy điều đó? Chồng các danh mục của bạn lại và theo dõi tỷ phần của mỗi danh mục trong tổng thể mở rộng hay thu hẹp theo thời gian, để một con nước dâng và một cơ cấu đang dịch chuyển cùng kể câu chuyện của chúng.

Example
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 ​

  • Cơ cấu theo thời gian, khi tổng thể cũng quan trọng. Các dải chồng lên nhau cho thấy tỷ trọng từng nhóm, còn mép trên vẽ ra tổng: vừa thấy nước lên, vừa thấy cơ cấu dịch chuyển, tất cả trong một hình.
  • Kể câu chuyện "cơ cấu đang thay đổi". Một dải mỏng dần trong khi tổng vẫn tăng là thông điệp không bảng tính nào truyền đạt nhanh bằng: rất hợp với báo cáo doanh thu theo sản phẩm hay lưu lượng theo kênh.
  • Khi thứ hạng đổi chỗ, hãy đổi biểu đồ. Nếu câu chuyện là ai vượt ai, biểu đồ dải thể hiện các cú hoán đổi rõ ràng hơn; còn nếu chỉ xét một thời điểm, biểu đồ tròn là đủ.
  • Tỷ phần, tỷ lệ hay chỉ số không cộng dồn được. Vẫn dùng vùng, nhưng cho chúng chồng lấn thay vì xếp chồng: stacked: false, trình bày bên dưới.

Vùng chồng lấn ​

Xếp chồng chỉ có ý nghĩa khi các chuỗi cộng lại thành một tổng thể có nghĩa. Tỷ phần, tỷ lệ và chỉ số thì không: một hộ gia đình có thể có cả máy tính xách tay lẫn điện thoại thông minh, nên tỷ lệ sở hữu cộng lại vượt quá 100% và mép trên của biểu đồ chồng sẽ vẽ ra một con số tổng chẳng ai đọc được. Đặt stacked: false, mỗi vùng sẽ đi từ 0 lên đến giá trị của chính nó, dưới dạng một lớp tô trong mờ với một đường chạy dọc mép trên.

Example
canvas · responsive
Tìm hiểu thêm: Hướng dẫn Insights·Hướng dẫn DevTools
tsx
<AreaChart {...props} stacked={false} />
vue
<AreaChart :options="{ ...props, stacked: false }" />
svelte
<div use:areaChart={{ ...props, stacked: false }}></div>
ts
applyAreaChartProps(this.c.nativeElement, { ...props, stacked: false });
html
<michi-vz-area-chart id="c"></michi-vz-area-chart>
<script>
  // thuộc tính HTML chỉ bật được chế độ xếp chồng: hãy đặt property để tắt nó
  document.getElementById("c").stacked = false;
</script>
  • Trục y chạy đến giá trị đơn lẻ lớn nhất (làm tròn theo một mốc đẹp), không phải tổng của một hàng, và không bị nâng sàn lên 100, nên các tỷ phần nhỏ như 0,9 vẫn giữ được chiều cao. yAxisDomain tường minh hoặc forcePercentageScale vẫn được ưu tiên.
  • Vùng lớn hơn được vẽ trước, để vùng nhỏ hơn nằm phía trên và vẫn nhìn thấy được. Thứ tự chú giải và màu sắc không đổi.
  • Vùng nào cũng phủ phần đáy của biểu đồ, nên khi di chuột, biểu đồ chọn chuỗi có mép trên gần con trỏ nhất.
  • Đường mép trên lấy màu của vùng, kể cả màu mà CSS của bạn đặt trên .area[data-label-safe], ở cả ba trình kết xuất.
  • stackOffset: "expand" chuẩn hóa một chồng, nên ở đây nó không có việc gì để làm: nó bị bỏ qua, kèm một cảnh báo dữ liệu ignored-option.

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

AreaChart có tùy chọn renderer="webgpu" để vẽ các dải chồng 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.

⚗️ 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 · ~7,500 points… detecting

Trục thời gian liên tục & các mốc không có dữ liệu ​

Trục x luôn giữ kỳ đầu tiên và cuối cùng và nghiêng / thu gọn các nhãn dày đặc xuống còn ~5. Chọn dùng fillPeriodTicks để vẽ một mốc cho mỗi tháng trong phạm vi; các tháng không có dữ liệu sẽ hiển thị mờ kèm tooltip khi di chuột "no data". Bật/tắt nó:

Toggle it on: the empty months appear as faded ticks. Hover one to see its "no data" tooltip. First and last periods are always kept, either way.

Tùy chỉnh qua noDataTickTooltip(epochMs) (nội dung tooltip) và noDataTickColor (hoặc biến CSS --michi-vz-tick-nodata).

tsx
<AreaChart
  {...props}
  xAxisDataType="date_monthly"
  fillPeriodTicks
  noDataTickTooltip={() => "No data reported for this month"}
  noDataTickColor="#c0392b"
/>
vue
<AreaChart :options="{ ...props, fillPeriodTicks: true, noDataTickTooltip: () => 'No data' }" />
svelte
<div use:areaChart={{ ...props, fillPeriodTicks: true, noDataTickTooltip: () => 'No data' }}></div>
ts
applyAreaChartProps(this.c.nativeElement, {
  ...props,
  fillPeriodTicks: true,
  noDataTickTooltip: () => "No data",
});
html
<michi-vz-area-chart id="c" fill-period-ticks no-data-tick-color="#c0392b"></michi-vz-area-chart>
<script>
  document.getElementById("c").noDataTickTooltip = () => "No data reported";
</script>

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:

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

<AreaChart
  ref={ref}
  {...props}
  progressiveDraw={{ durationMs: 2000 }}
/>;
// ref.current?.replay() chạy lại hiệu ứng khi cần
vue
<AreaChart :options="{ ...props, progressiveDraw: { durationMs: 2000 } }" />
svelte
<div use:areaChart={{ ...props, progressiveDraw: { durationMs: 2000 } }}></div>
ts
applyAreaChartProps(this.c.nativeElement, {
  ...props,
  progressiveDraw: { durationMs: 2000 },
});
html
<michi-vz-area-chart id="c"></michi-vz-area-chart>
<script>
  const el = document.getElementById("c");
  el.progressiveDraw = { durationMs: 2000 };
  // el.replay() chạy lại hiệu ứng
</script>
  • durationMs và easing ("linear", "easeOutQuad", "easeInOutCubic", hoặc hàm (t) => t tự viết) quyết định nhịp vẽ.
  • autoplay: false hiển thị biểu đồ vẽ sẵn đầy đủ; gọi replay() (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: true chạ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.

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

Dữ liệu vốn đã trải dài qua nhiều năm nên không cần gắn thêm gì cả. Bật timeline: nút play và thanh tua riêng của biểu đồ chạy qua các năm đó, mỗi bước các dải chồng chỉ vẽ đến năm đang active rồi khi chạy tiếp sẽ nối dài mượt mà thêm ra. Kéo lùi thanh tua thì các dải cũng co lại tương ứng. Hover chỉ soi được phần đã thực sự vẽ ra. Mặc định tắt - không bật thì biểu đồ giữ nguyên.

Bấm nút play dưới biểu đồ: biểu đồ vẽ dần đến từng năm khi chạy qua. Kéo thanh tua để nhảy đến năm bất kỳ.

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

<AreaChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / seekIndex(i) / stepForward()
vue
<AreaChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" />
svelte
<div use:areaChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>
ts
applyAreaChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });
html
<michi-vz-area-chart id="c"></michi-vz-area-chart>
<script>
  const el = document.getElementById("c");
  el.timeline = { speedMs: 1000, loop: true };
  // el.getTimeline() -> play() / pause() / seek(year) / seekIndex(i)
</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.
  • Controller headless luôn sẵn sàng: chart.timeline() cho play() / pause() / toggle() / seek(period) / seekIndex(i) / stepForward() / stepBack(), kèm onStep và formatPeriod trong config khi cần tự dựng UI.
  • seek(period) tìm kỳ trước, so sánh dạng chữ, nên seek(2021) và seek("2021") đều tới 2021 dù ngày trong dữ liệu là số hay chuỗi. Số chỉ được hiểu là vị trí (0 = kỳ đầu) khi không có kỳ nào khớp, còn chuỗi không khớp kỳ nào thì không làm gì. seekIndex(i) luôn đi theo vị trí, giống thanh tua có sẵn.
  • Giá trị trượt mượt giữa các năm theo mặc định (interpolate); đặt interpolate: false để cắt thẳng. Khi bật reduced motion, biểu đồ luôn cắt thẳng.
  • timeline được ưu tiên hơn progressiveDraw khi cả hai cùng được bật trên một biểu đồ.

Cách dùng ​

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

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

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

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

// component (uses CUSTOM_ELEMENTS_SCHEMA)
// template: <michi-vz-area-chart #c></michi-vz-area-chart>
applyAreaChartProps(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-area-chart id="c"></michi-vz-area-chart>
<script>
  Object.assign(document.getElementById("c"), props); // dataSet/series, title, …
</script>
ts
import { mountAreaChart } from "@michi-vz/core";

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

API ​

Các prop được định kiểu là AreaChartProps 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.