GrChartArea
Overview
When to take it
- the whole and the contribution of its parts over time — revenue by channel, traffic by
source, time by stage:
stackedlays every series on the sum of the previous ones; - a single series whose volume matters — a fill down to zero reads as “how much in total” rather than “at what level”;
- shares changing over time —
stacked: '100%'normalises every position to one, and the redistribution is visible rather than the growth; - series that have to show through — when overlaid without stacking,
fill: 'auto'gives a gradient, and the lower series stays visible.
When to take something else
| Need | Take |
|---|---|
| Show the course of a value, the volume does not matter | GrChartLine |
| Compare quantities across categories | GrChartBar |
| Show the composition of a whole at one moment | GrChartPie |
| Compare the shape of a profile across several axes | GrChartRadar |
| A trend in a table cell | GrSparkline |
With more than seven or eight categories a stack stops being readable: the bands become thinner than
their labels, and they can no longer be compared by eye. That is a sign that
GrChartBar with grouping is needed, or fewer series.
Examples 5 each with a live demo and its code
Revenue by channel: the upper edge is the total
stackedputs every channel on the sum of the previous ones, so the upper edge reads as the total. The plan is areferencesline, not a series, andinclude-references-in-domainkeeps it on screen. Hiding a channel in the legend (v-model:hidden-series) drops it from the stack and from the figure above. A new period showsloading, an empty one theemptyslot, and thetooltipslot names money in euros.Revenue in September€399k+4.7%Chart data X Direct sales Partners Marketplace Apr 171 108 46 May 175 112 52 Jun 182 115 55 Jul 190 121 57 Aug 196 124 61 Sep 204 129 66 Plan €420k: 420 A day of server load with zoom
1,440 points on a
timeaxis:decimatethins the drawn path tomax-points, while the cursor, the keyboard and the hidden table — capped bydata-table-max-rows— still know every minute.zoom="both"takes a brush and the wheel, andv-model:x-windowlets «Whole day» reset it. The overload zone is a reference band;point-clickpins a moment to the incident.api-eu-1 · CPU5 October, UTC · drag over the chart or scroll to zoom inChart data X CPU Oct 5, 2026, 12:00 AM 12 Oct 5, 2026, 12:14 AM 20.5 Oct 5, 2026, 12:36 AM 9.7 Oct 5, 2026, 12:39 AM 18.7 Oct 5, 2026, 12:58 AM 9 Oct 5, 2026, 01:01 AM 18.1 Oct 5, 2026, 01:23 AM 7.6 Oct 5, 2026, 01:26 AM 16.7 Oct 5, 2026, 01:48 AM 6.5 Oct 5, 2026, 01:51 AM 15.6 Oct 5, 2026, 02:13 AM 5.6 Oct 5, 2026, 02:16 AM 14.7 Oct 5, 2026, 02:35 AM 5.9 Oct 5, 2026, 02:41 AM 14.2 Oct 5, 2026, 03:00 AM 5.5 Oct 5, 2026, 03:03 AM 14.7 Oct 5, 2026, 03:25 AM 5.5 Oct 5, 2026, 03:28 AM 14.7 Oct 5, 2026, 03:50 AM 5.7 Oct 5, 2026, 03:53 AM 14.9 Oct 5, 2026, 04:12 AM 6.9 Oct 5, 2026, 04:18 AM 15.4 Oct 5, 2026, 04:37 AM 7.6 Oct 5, 2026, 04:43 AM 16.2 Oct 5, 2026, 05:02 AM 8.6 Oct 5, 2026, 05:05 AM 18 Oct 5, 2026, 05:27 AM 9.9 Oct 5, 2026, 05:30 AM 19.2 Oct 5, 2026, 05:49 AM 12 Oct 5, 2026, 05:55 AM 20.7 Oct 5, 2026, 06:14 AM 13.6 Oct 5, 2026, 06:20 AM 22.4 Oct 5, 2026, 06:39 AM 15.5 Oct 5, 2026, 06:45 AM 24.3 Oct 5, 2026, 07:04 AM 17.5 Oct 5, 2026, 07:07 AM 26.9 Oct 5, 2026, 07:26 AM 20.2 Oct 5, 2026, 07:32 AM 29.1 Oct 5, 2026, 07:51 AM 22.4 Oct 5, 2026, 07:57 AM 31.4 Oct 5, 2026, 08:16 AM 24.7 Oct 5, 2026, 08:22 AM 33.7 Oct 5, 2026, 08:41 AM 27.1 Oct 5, 2026, 08:47 AM 36.1 Oct 5, 2026, 09:03 AM 30 Oct 5, 2026, 09:09 AM 39 Oct 5, 2026, 09:28 AM 32.4 Oct 5, 2026, 09:34 AM 41.4 Oct 5, 2026, 09:53 AM 34.8 Oct 5, 2026, 09:59 AM 43.8 Oct 5, 2026, 10:18 AM 37.1 Oct 5, 2026, 10:24 AM 46.1 Oct 5, 2026, 10:40 AM 39.9 Oct 5, 2026, 10:46 AM 48.8 Oct 5, 2026, 11:05 AM 42 Oct 5, 2026, 11:11 AM 50.8 Oct 5, 2026, 11:30 AM 43.9 Oct 5, 2026, 11:36 AM 52.8 Oct 5, 2026, 11:55 AM 45.7 Oct 5, 2026, 12:01 PM 54.5 Oct 5, 2026, 12:17 PM 47.9 Oct 5, 2026, 12:26 PM 56 Oct 5, 2026, 12:42 PM 49.3 Oct 5, 2026, 12:48 PM 58 Oct 5, 2026, 01:07 PM 50.5 Oct 5, 2026, 01:13 PM 59.1 Oct 5, 2026, 01:32 PM 51.4 Oct 5, 2026, 01:48 PM 54.2 Oct 5, 2026, 02:00 PM 91 Oct 5, 2026, 02:12 PM 98 Oct 5, 2026, 02:22 PM 87 Oct 5, 2026, 02:30 PM 53.6 Oct 5, 2026, 02:39 PM 61.2 Oct 5, 2026, 02:55 PM 53.6 Oct 5, 2026, 03:04 PM 61.1 Oct 5, 2026, 03:20 PM 53.3 Oct 5, 2026, 03:34 PM 62.6 Oct 5, 2026, 03:45 PM 52.7 Oct 5, 2026, 03:51 PM 61 Oct 5, 2026, 04:10 PM 51.9 Oct 5, 2026, 04:16 PM 60 Oct 5, 2026, 04:32 PM 51.7 Oct 5, 2026, 04:41 PM 58.9 Oct 5, 2026, 04:57 PM 50.4 Oct 5, 2026, 05:11 PM 59.1 Oct 5, 2026, 05:22 PM 48.7 Oct 5, 2026, 05:36 PM 57.3 Oct 5, 2026, 05:47 PM 46.9 Oct 5, 2026, 05:53 PM 54.8 Oct 5, 2026, 06:09 PM 45.9 Oct 5, 2026, 06:18 PM 52.7 Oct 5, 2026, 06:34 PM 43.7 Oct 5, 2026, 06:48 PM 52 Oct 5, 2026, 06:59 PM 41.3 Oct 5, 2026, 07:13 PM 49.5 Oct 5, 2026, 07:24 PM 38.8 Oct 5, 2026, 07:30 PM 46.5 Oct 5, 2026, 07:46 PM 37.2 Oct 5, 2026, 07:55 PM 43.9 Oct 5, 2026, 08:11 PM 34.5 Oct 5, 2026, 08:25 PM 42.5 Oct 5, 2026, 08:36 PM 31.7 Oct 5, 2026, 08:50 PM 39.7 Oct 5, 2026, 09:01 PM 28.9 Oct 5, 2026, 09:15 PM 36.9 Oct 5, 2026, 09:23 PM 27.2 Oct 5, 2026, 09:32 PM 33.8 Oct 5, 2026, 09:48 PM 24.4 Oct 5, 2026, 10:02 PM 32.5 Oct 5, 2026, 10:13 PM 21.7 Oct 5, 2026, 10:27 PM 29.8 Oct 5, 2026, 10:38 PM 19 Oct 5, 2026, 10:52 PM 27.2 Oct 5, 2026, 11:00 PM 17.6 Oct 5, 2026, 11:17 PM 24.8 Oct 5, 2026, 11:25 PM 15.2 Oct 5, 2026, 11:39 PM 23.6 Oct 5, 2026, 11:50 PM 13 Oct 5, 2026, 11:58 PM 13.5 Oct 5, 2026, 11:59 PM 19.8 Overload: 80 to 100 Showing 120 of 1440 points — the ones the chart draws. Arrow keys walk all of them. Click a point to add it to the incident.
Two charts with one cursor
v-model:active-indexties the pair: hovering either moves the cursor in both, the tooltips name the same minute, and the readout above sums it up in one line. Requests per minute and milliseconds differ by two orders of magnitude —dual-axiswithy-domain-right,y-tick-format-rightandvalue-format-rightis a deliberate choice here, not a default.size="sm"keeps the pair compact.Checkout API · last hour13:59 · 1,232 requests · p95 140 ms · 1 errors Chart data X Requests / min (left axis) p95 latency, ms (right axis) 13:00 1,100 165 13:01 1,176 167 13:02 1,160 169 13:03 1,235 170 13:04 1,217 170 13:05 1,199 170 13:06 1,271 169 13:07 1,250 167 13:08 1,320 165 13:09 1,295 163 13:10 1,269 160 13:11 1,333 156 13:12 1,303 152 13:13 1,363 148 13:14 1,328 144 13:15 1,291 140 13:16 1,344 136 13:17 1,303 132 13:18 1,352 127 13:19 1,306 124 13:20 1,259 120 13:21 1,302 117 13:22 1,252 115 13:23 1,200 113 13:24 1,238 111 13:25 1,184 110 13:26 1,221 110 13:27 1,165 110 13:28 1,109 111 13:29 1,146 113 13:30 1,090 115 13:31 1,126 117 13:32 1,072 120 13:33 1,018 124 13:34 1,057 128 13:35 1,006 132 13:36 1,048 136 13:37 1,000 140 13:38 953 144 13:39 1,001 149 13:40 958 153 13:41 1,010 156 13:42 972 320 13:43 936 323 13:44 995 325 13:45 963 327 13:46 934 329 13:47 999 330 13:48 974 170 13:49 1,042 170 13:50 1,020 169 13:51 1,000 167 13:52 1,073 165 13:53 1,055 163 13:54 1,130 160 13:55 1,113 156 13:56 1,097 152 13:57 1,173 148 13:58 1,157 144 13:59 1,232 140 Chart data X Errors / min 13:00 0 13:01 3 13:02 2 13:03 1 13:04 0 13:05 3 13:06 2 13:07 1 13:08 0 13:09 3 13:10 2 13:11 1 13:12 0 13:13 3 13:14 2 13:15 1 13:16 0 13:17 3 13:18 2 13:19 1 13:20 0 13:21 3 13:22 2 13:23 1 13:24 0 13:25 3 13:26 2 13:27 1 13:28 0 13:29 3 13:30 2 13:31 1 13:32 0 13:33 3 13:34 2 13:35 1 13:36 0 13:37 3 13:38 2 13:39 1 13:40 0 13:41 3 13:42 18 13:43 23 13:44 21 13:45 19 13:46 24 13:47 22 13:48 0 13:49 3 13:50 2 13:51 1 13:52 0 13:53 3 13:54 2 13:55 1 13:56 0 13:57 3 13:58 2 13:59 1
API generated from the package · cannot drift
Props
| Prop | Type | default | Description |
|---|---|---|---|
xScale | GrChartScaleKind | undefined | undefined | Type of the X axis. Not set — inferred from the data. |
stacked | boolean | "100%" | undefined | false | Stack the series: each band lies on the sum of the previous ones. `'100%'` additionally normalizes every position to one — the band then shows how the **distribution** changed, not the magnitudes. The own values of the points are left alone: the tooltip and the table say "forty", not "a third". |
fill | "auto" | "gradient" | "solid" | undefined | undefined | Fill. `auto` — a gradient for an overlay, solid for a stack. |
height | number | undefined | undefined | The canvas height in pixels; 256 by default. The data, the loading state and the empty state all take up the same height. |
width | number | undefined | 640 | Declared width: the first render goes from it, after that the width is measured. |
yDomain | readonly [number | null, number | null] | undefined | undefined | The bounds of the value axis `[min, max]`; `null` in place of a bound — it is taken from the data. |
includeZero | boolean | undefined | false | Extend the value axis to zero; off by default — the axis fits tightly around the data. |
references | readonly GrChartReference[] | undefined | undefined | Reference lines and bands: a threshold, a plan, an acceptable corridor. Not a series and never counted as one: it does not get into the legend, does not spend a palette index, does not join the stack and goes into the hidden table as a note, not as a data row. |
includeReferencesInDomain | boolean | undefined | false | Include the references in the axis domain. Off by default, and deliberately so: a threshold of `1.0` with data around `0.03` would stretch the axis so far that the data itself would collapse into a line. |
dualAxis | boolean | undefined | false | Show a second value axis on the right. Without it, series with `axis: 'right'` land on the left one. Turning it on is deliberate: two axes let you fit any two series to a visible correlation, and that must be a decision of the chart author, not a side effect of a field in the data. |
yDomainRight | readonly [number | null, number | null] | undefined | undefined | The bounds of the right value axis with `dualAxis`; `null` in place of a bound — it is taken from the data. |
yTickFormatRight | ((value: number) => string) | undefined | undefined | The tick label of the right axis. Unset — a number formatted by the locale. |
valueFormatRight | GrChartNumberFormat | undefined | undefined | The value format of the right-axis series in the tooltip, the hidden table and announcements. |
xTickCount | number | undefined | undefined | The desired number of ticks on the X axis; 6 by default. For categories — the cap on the number of labels (unset — 12). Labels that do not fit the width are thinned out. |
yTickCount | number | undefined | 5 | The desired number of ticks on the value axis; 5 by default. The step stays "round", so there may be one tick fewer. |
xTickFormat | ChartTickFormat | undefined | undefined | The tick label of the X axis: `(value, kind) => string`. Unset — the category, or a date or number formatted by the locale. |
yTickFormat | ((value: number) => string) | undefined | undefined | The tick label of the value axis, for example `value => value + '%'`. The space for the axis is measured from the formatted label. |
valueFormat | GrChartNumberFormat | undefined | undefined | The value format in the tooltip, the hidden table and announcements: decimal places and separators. |
curve | GrChartCurve | undefined | undefined | The shape of the line between points: `linear` (the default), `smooth` or `step`. Smoothing does not invent values between measurements. |
showPoints | "auto" | "never" | "always" | undefined | "auto" | Point markers: `auto` (the default) — only on a sparse series, `always` or `never`. |
showGrid | "y" | "both" | "x" | "none" | undefined | undefined | Grid lines: `y` (the default) — along the value ticks, `x`, `both` or `none`. |
showLegend | boolean | "auto" | undefined | undefined | The series legend: `auto` (the default) — when there is more than one series; `true` and `false` — always and never. |
legendPosition | "bottom" | "top" | undefined | undefined | Where the legend sits: `bottom` (the default) or `top`. |
tooltip | boolean | undefined | undefined | The panel with the values under the cursor; `true` by default. `false` hides only the panel: the cursor, the active marks, `update:activeIndex` and `pointHover` keep working while the chart is interactive. |
activeIndexv-model | number | null | undefined | undefined | Cursor — `v-model:activeIndex`. Keeps a pair of charts in sync. |
loading | boolean | undefined | false | Loading: a skeleton of the same height stands in place of the plot area, and the space for the axes is already reserved. |
empty | boolean | undefined | undefined | The empty state, set explicitly. Unset — the chart is empty when there is nothing to draw. |
emptyText | string | undefined | undefined | The placeholder text of an empty chart. Unset — "No data" from the package translations. |
dataTable | "hidden" | "visible" | "off" | undefined | undefined | The data table: `hidden` (the default) — for the screen reader only, `visible` — under the chart, `off` — no table. |
interactive | boolean | undefined | true | `false` — the chart becomes a picture: no focus, no tooltip, no keyboard. |
size | "md" | "xs" | "sm" | "lg" | undefined | undefined | The type size of the axis and legend labels on the core control scale; by default — the size from `GrConfigProvider`. |
locale | string | undefined | undefined | The BCP-47 locale for numbers and dates. Unset — the language of the application translations, otherwise `en`. |
ariaLabel | string | undefined | undefined | The accessible name of the chart. Unset — a summary the component assembles: the chart type and the amount of data. |
ariaDescription | string | undefined | undefined | A description for the screen reader — the conclusion the chart is shown for; it adds to the summary rather than replacing it. |
decimate | "auto" | "never" | "always" | undefined | undefined | When to thin the series for the drawing (LTTB). `'auto'` kicks in only when there are more points than the budget: above two vertices per pixel SVG will not show what the algorithm would have dropped anyway, and it pays for that with the length of the `d` string. Thinned points go **only** into the drawing — the cursor, the keyboard, the tooltip and the hidden table know the full series. |
maxPoints | number | undefined | undefined | Point budget per series. Not set — computed from the width of the plot area. |
canvasThreshold | number | undefined | GR_CHART_CANVAS_THRESHOLD | The threshold above which marks are not drawn even with `showPoints: 'always'`. On `'auto'` with the defaults it has no effect: that one cuts marks off earlier, at `AUTO_MARKERS_LIMIT`. It starts to matter if you set the threshold below it. |
zoom | GrChartZoom | undefined | undefined | Which gestures the user changes the visible window with. By default — none. Driven by the pointer only; the chart has no keyboard path to the window. That is why the window is exposed as `v-model:xWindow` — the application must give its own accessible path to it: a reset button, range fields. |
xWindowv-model | readonly [GrChartXValue, GrChartXValue] | null | undefined | undefined | The visible window along the abscissa — `v-model:xWindow`. `null` — the whole series. The window **selects data**, it does not crop the drawing: positions, the cursor, the keyboard, the hidden table and the value axis extent are computed from it. Thinning is the opposite case, it is invisible to the drawing and does not touch the full series. Hence the consequence: `activeIndex` addresses the current window. |
dataTableMaxRows | number | "auto" | undefined | undefined | The row ceiling of the hidden data table. `'auto'` (the default) — as many as there are vertices in the drawing: above the ceiling the table prints the same points that are drawn and says so with a note. A number sets its own ceiling, `Infinity` removes it altogether, `dataTable: 'off'` removes the table entirely — which of these is needed is up to the application. |
seriesrequired | readonly number[] | readonly GrChartSeries[] | — | Series or a bare row of numbers — then `x` becomes the ordinal index. |
Slots
| Slot | Type | Description |
|---|---|---|
tooltip | { active: GrChartActivePoint; formatValue: (value: number | null) => string; } | Custom content for the tooltip panel instead of the standard one; the data of the active position comes in the slot props. |
legend | { series: readonly NormalizedSeries[]; toggle: (id: string) => void; } | A custom legend instead of the standard one. It is not drawn for an empty chart. |
empty | any | A custom placeholder for an empty chart instead of "No data"; it takes up the same `height`. |
header | any | A header above the chart, inside its root — above a legend placed on top. |
Events
| Event | Signature | Description |
|---|---|---|
update:hiddenSeries | (event: "update:hiddenSeries", value: string[]): void | — |
update:activeIndex | (event: "update:activeIndex", value: number | null): void | — |
update:xWindow | (event: "update:xWindow", value: GrChartXWindow | null): void | — |
pointClick | (event: "pointClick", value: GrChartActivePoint): void | — |
pointHover | (event: "pointHover", value: GrChartActivePoint | null): void | — |
legendToggle | (event: "legendToggle", value: { seriesId: string; hidden: boolean; }): void | — |
Methods
| Methods | Signature | Description |
|---|---|---|
element | HTMLElement | null | The root element — for measurements and scrolling in the consumer. |
Types
Every exported type of the subpath, grouped by the file it lives in. Names, generics and bodies come from the package’s declaration files and cannot drift.
import type { GrChartAreaConfigurableProps, GrChartSize, GrChartAreaProps } from '@feugene/granularity-charts/components/GrChartArea'Filedist/types/components/GrChartArea/config.d.ts
const grChartAreaConfig: import("@feugene/granum/contract").GranumComponentDescriptor<"GrChartArea">;Filedist/types/components/GrChartArea/defaults.d.ts
interface GrChartAreaConfigurableProps {
decimate: 'auto' | 'always' | 'never';
maxPoints: number;
zoom: GrChartZoom;
dataTableMaxRows: number | 'auto';
size: GrChartSize;
height: number;
curve: GrChartCurve;
fill: 'auto' | 'gradient' | 'solid';
showGrid: 'both' | 'x' | 'y' | 'none';
showLegend: boolean | 'auto';
legendPosition: 'top' | 'bottom';
tooltip: boolean;
dataTable: 'hidden' | 'visible' | 'off';
}Filedist/types/components/GrChartFrame/chartFrameStyles.d.ts
type GrChartSize = GrComponentSize;Filedist/types/components/GrChartArea/safelist.d.ts
const grChartAreaSafelist: string[];Filedist/types/components/GrChartArea/GrChartArea.vue.d.ts
interface GrChartAreaProps {
series: readonly GrChartSeries[] | readonly number[];
xScale?: GrChartScaleKind;
stacked?: boolean | '100%';
fill?: 'auto' | 'gradient' | 'solid';
height?: number;
width?: number;
yDomain?: readonly [number | null, number | null];
includeZero?: boolean;
references?: readonly GrChartReference[];
includeReferencesInDomain?: boolean;
dualAxis?: boolean;
yDomainRight?: readonly [number | null, number | null];
yTickFormatRight?: (value: number) => string;interface GrChartAreaEmits {
(e: 'update:hiddenSeries', value: string[]): void;
(e: 'update:activeIndex', value: number | null): void;
(e: 'update:xWindow', value: GrChartXWindow | null): void;
(e: 'pointClick', value: GrChartActivePoint): void;
(e: 'pointHover', value: GrChartActivePoint | null): void;
(e: 'legendToggle', value: {
seriesId: string;
hidden: boolean;
}): void;
}Accessibility
APG pattern
При нескольких сериях ↑/↓ переключают читаемую серию
Full contract of the package: docs/keyboard.md ↗
Usage from the library prose
Above the threshold it is drawn by a canvas
The body of the chart can be drawn in two ways. Below the threshold it is SVG, above it a
<canvas>; the component chooses itself, and there is no “which renderer” prop.
The threshold is counted in drawn vertices rather than in points — canvasThreshold, 24,000 by
default. The difference is substantial: decimation cuts every series down to the limit of the
screen (about two vertices per pixel), so one series of a hundred thousand points is drawn as 2,400
vertices and costs milliseconds, while twenty series of 2,400 — the same 48,000 points — cost
sixteen, that is, a whole frame.
The measured numbers at a width of 1200px, 2,400 vertices per series:
| Series | SVG | Canvas |
|---|---|---|
| 1 | 1.1 ms | 0.4 ms |
| 8 | 6.7 ms | 0.6 ms |
| 20 | 16.3 ms | 1.7 ms |
canvasThreshold: 0 switches the canvas off entirely — for the case where the drawing has to stay
vector: printing, an SVG export, CSS of your own over the marks.
Accessibility does not change at all with the change of renderer. The cursor, the keyboard, the
tooltip and the hidden table work with the overlay and with the full series rather than with the
marks: the canvas does not exist for them, it is aria-hidden and does not catch the pointer. That
was the condition on which a second renderer was allowed at all.
What the canvas draws differently. The grid moves into it as well: the canvas lies under the
<svg> so that the axes and the active point stay on top — and the grid has to stay under the
series. In an area chart the gradient fill becomes solid: the canvas does not understand url(#…),
and across twenty areas a gradient reads as a mess anyway.
The fill is chosen by the mode rather than by taste
fill: 'auto' means a gradient for an overlay and a dense fill for a stack. The reason is different
in each case: overlaid series need to show through one another, while the bands of a stack stand
flush, and a gradient would blur the boundary between two neighbours — that is, the very place where
one part ends and another begins.
The value of a point stays its own
In a stack the bounds of the band arrive on a point as separate fields, but its own y is not
touched. The tooltip, the hidden table and the live region say “partners — one hundred and ninety”
rather than “six hundred and fifty, because retail lies underneath”.
A hidden series drops out of the stack, and the ones above it come down. The value axis is computed from the tops of the bands and always includes zero: a stack from a non-zero base lies about the proportions.
A stack is built from the full series. A gap is the only place where it is not entirely honest: the sum at that position is understated. Carrying the last known value forward would be worse — that would be drawing data that did not exist.
A hundred per cent answers a different question
stacked: true shows the quantities and their sum, and stacked: '100%' only the distribution: the
sum at every position equals one, and the band says how the shares changed rather than how much
there was in total. One question has to be chosen: the reader does not know whether to compare the
heights of the bands or their shares.
What is normalised is the drawing, not the data. The axis moves to percentages, while the
tooltip, the hidden table and the live region still name the absolute quantities. A position with a
zero sum has nothing to normalise against — there there is a zero rather than a NaN.
A long series is shortened in the drawing rather than in the data
decimate: 'auto' (the default) thins out the vertices of the path when there are more points than the screen can
show. In a stack the set of abscissas is shared across all of the series — otherwise the bottom of the upper band
would be interpolated over one set of X values and the top of the lower one over another, and the fill would come
apart at the seams. The cursor, the keyboard, the tooltip and the hidden table know the full series; the details —
../model.md, the section “Decimation is a projection, not the data”.
Zooming along the abscissa
zoom switches the window on: 'brush' is a drag across the canvas, 'wheel' is the wheel, and
'both' is both. It is off by default.
<GrChartArea v-model:x-window="window" :series="series" zoom="both" />
<GrButton :disabled="window === null" @click="window = null">
The whole series
</GrButton>The window selects the data rather than cropping the drawing: the positions, the cursor, the keyboard, the hidden table and the span of the value axis are computed from it. The practical consequence is that zooming reveals the fine structure that on the full series lies as a solid hatching: the decimation budget is computed from the width of the area, and there are fewer points in the window, so more vertices fall to each of them.
The keyboard works whenever zoom is on: +/- zoom towards the active point, Shift+arrows
shift the window, and 0 returns the whole series. The union of the prop enumerates only pointer
gestures — zooming has no switchable keyboard by design (../a11y.md, the section
on zooming).
v-model:x-window is not mandatory — without a binding the chart zooms by itself. It is bound for
something else: a synchronised pair of charts, a reset button next to the canvas, keeping the zoom in
the address bar.
The bounds are accepted in the same form as the abscissas of the points (Date, an ISO string, a
number) and go out as numbers. With a window set, activeIndex addresses it rather than the
whole series.
A threshold is drawn as a reference, not as a series
A plan, a norm and the limit of the acceptable are the references prop rather than a series made of a constant: a
constant series would enter the legend, would stretch the axis and would travel into the table as data. The details —
../model.md, the section “A reference is not a series”.
Two axes are a deliberate decision
Series of different orders (money and units) are readable on one chart only with two axes, and two
axes let any pair be fitted to a visible correlation. That is why the axis: 'right' of a series
does not work until dualAxis is switched on. The invariants —
../model.md, the section “The second value axis”.
Props in this example