Skip to content

Lijndiagram

Trends

"Hoe is dit door de tijd heen verlopen, en waar kan ik de data niet vertrouwen?" Eén reeks of vijftig, met ontbrekende periodes weergegeven als streepjes zodat een hiaat in de rapportage nooit als een echte dip wordt gelezen - plus een optionele canvas-renderer (LTTB-gedecimeerd voor grote datasets) wanneer het aantal punten in de duizenden loopt.

Example
canvas · responsive
Ga dieper: Insights-gids·DevTools-gids

De grafiek hierboven is dezelfde engine in elk framework - alleen de integratiecode hieronder verschilt.

Wanneer kies je deze

  • Het verloop van een KPI volgen. Omzet per maand, gebruikers per week, latentie per uur - overal waar "hoe is dit bewogen?" de vraag is, beantwoordt de lijn die sneller dan welke tabel dan ook.
  • Een handvol reeksen op één schaal vergelijken, met hiaten in de rapportage eerlijk weergegeven: een ontbrekende periode wordt getoond als een streepje, nooit als een valse dip waar een directielid op zou kunnen reageren.
  • Ook bij grote datasets. Duizenden punten blijven soepel dankzij de optionele canvas-renderer (LTTB-decimatie). Maar als het verhaal een voorspelling is in plaats van historie, toont het waaierdiagram de bandbreedte, niet alleen de lijn.

Veel data op WebGPU Experimenteel

De optionele renderer="webgpu" van het lijndiagram tekent de lijn-/markergeometrie op de GPU, terwijl assen, labels en tooltips op de SVG-laag blijven; dit is capability-gated met automatische terugval naar canvas wanneer WebGPU niet beschikbaar is.

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

Detectie van hiaten

Een ontbrekende periode wordt weergegeven als een gestreept segment - stel dit per punt in met certainty: false, of laat detectGaps het automatisch afleiden. Hier slaat één reeks een rapportageperiode over:

Example
canvas · responsive
Ga dieper: Insights-gids·DevTools-gids

Doorlopende tijdlijn en no-data-ticks

Standaard is de x-as op twee manieren eerlijk over tijd: de eerste en laatste periode worden nooit weggelaten (zelfs niet als ze op een "onronde" maand vallen die d3 anders zou overslaan), en overvolle labels kantelen naar -45° en worden vervolgens uitgedund tot ongeveer 5 - waarbij beide uiteinden altijd behouden blijven.

Schakel fillPeriodTicks in en de as tekent een tick voor elke periode binnen het bereik, niet alleen die met data. Maanden zonder waarde worden vervaagd weergegeven; hover erover om het hiaat toe te lichten. Zet de schakelaar om:

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.

Pas het aan: noDataTickTooltip(epochMs) retourneert de tooltiptekst (platte string of gesaniteerde HTML), en noDataTickColor (of de CSS-variabele --michi-vz-tick-nodata) stelt de vervaagde kleur in.

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

Speel door de jaren heen

De data beslaat al meerdere jaren, dus er is niets te taggen. Zet timeline aan: de eigen afspeelknop en scrubber van de grafiek stappen door die jaren - bij elke stap tekent de lijn zich alleen tot het actieve jaar, en tijdens het afspelen loopt die lijn vloeiend verder door. Scrub je terug, dan trekt de lijn zich weer terug. Hoveren toont alleen wat al echt getekend is. Dit is de interactieve versie, jaar voor jaar: scrub of stap door de echte jaren in de data. Voor een eenmalige cinematische sweep, zie Progressief tekenen en tiplabels hieronder. Standaard uit - zonder opt-in verandert er niets.

Druk op de afspeelknop onder de grafiek: de grafiek tekent zich verder door tot elk jaar terwijl hij speelt. Sleep de scrubber om naar een jaar te springen.

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

<LineChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / stepForward()
vue
<LineChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" />
svelte
<div use:lineChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>
ts
applyLineChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });
html
<michi-vz-line-chart id="c"></michi-vz-line-chart>
<script>
  const el = document.getElementById("c");
  el.timeline = { speedMs: 1000, loop: true };
  // el.getTimeline() -> play() / pause() / seek(year)
</script>
  • speedMs bepaalt het tempo, loop begint opnieuw, autoplay: true start bij mounten, showControl: false verbergt de ingebouwde balk.
  • De headless controller is altijd beschikbaar: chart.timeline() biedt play() / pause() / toggle() / seek(period) / stepForward() / stepBack(), plus onStep en formatPeriod in de config voor eigen UI.
  • Waarden glijden standaard tussen jaren (interpolate); zet interpolate: false voor harde overgangen. Met reduced motion is de overgang altijd hard.
  • timeline wint het van progressiveDraw wanneer beide op dezelfde grafiek staan.
  • tipLabel: true in de timeline-config laat een label meebewegen met de groeiende lijnpunt tijdens het afspelen - hetzelfde tiplabel als bij progressiveDraw, nu alleen aangestuurd door de scrubber in plaats van de eenmalige sweep.

Progressief tekenen en tiplabels

Laat de grafiek het verhaal in volgorde vertellen: met progressiveDraw tekent elke lijn zichzelf van het eerste tot het laatste jaar, met optioneel een tiplabel dat op het uiteinde van de lijn meebeweegt en de reeksnaam plus de actuele waarde toont, om uiteindelijk naast het lijneinde te blijven staan. Dit is een eenmalige cinematische sweep bij het mounten; voor interactief jaar-voor-jaar stappen met een scrubber, zie Speel door de jaren heen hierboven. Standaard uit - zonder opt-in verandert er niets.

Elke lijn groeit van het eerste naar het laatste jaar; het label volgt de punt en komt tot stilstand bij het lijneinde. Met reduced motion ingeschakeld verschijnt de grafiek meteen volledig getekend.

progressiveDraw: true gebruikt de standaardinstellingen (1200 ms, easeInOutCubic). Een configuratieobject verfijnt het gedrag:

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

<LineChart
  ref={ref}
  {...props}
  progressiveDraw={{ durationMs: 2000, tipLabel: true }}
/>;
// ref.current?.replay() speelt de animatie opnieuw af
vue
<LineChart :options="{ ...props, progressiveDraw: { durationMs: 2000, tipLabel: true } }" />
svelte
<div use:lineChart={{ ...props, progressiveDraw: { durationMs: 2000, tipLabel: true } }}></div>
ts
applyLineChartProps(this.c.nativeElement, {
  ...props,
  progressiveDraw: { durationMs: 2000, tipLabel: true },
});
html
<michi-vz-line-chart id="c"></michi-vz-line-chart>
<script>
  const el = document.getElementById("c");
  el.progressiveDraw = { durationMs: 2000, tipLabel: true };
  // el.replay() speelt de animatie opnieuw af
</script>
  • durationMs en easing ("linear", "easeOutQuad", "easeInOutCubic", of een eigen (t) => t-functie) bepalen het tempo.
  • tipLabel: true tekent het meebewegende label; { content: "name" | "value" | "both", format } beperkt of herschrijft de tekst. De getoonde waarde is altijd een echt datapunt, nooit een geïnterpoleerd getal.
  • autoplay: false rendert de grafiek volledig getekend; roep replay() aan (React-ref-handle, webcomponent-methode of de core-instantie) om de animatie op aanvraag te starten. replayOnUpdate: true herhaalt de animatie bij elke datawijziging.
  • Respecteert prefers-reduced-motion: de grafiek verschijnt dan meteen volledig getekend.
  • Tijdens de animatie stoppen het kruisdraadje en de tooltips bij de tekenrand, zodat hoveren nooit gegevens toont die nog niet getekend zijn. De webgpu-renderer slaat de animatie over.

Gebruik

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

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

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

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

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

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

Laadstatus en 'geen data'-status

Geef isLoading door terwijl je data-fetch nog bezig is; de engine toont een .mv-loading-overlay en zet data-mv-state="loading" op de host.

Wanneer de fetch niets oplevert, neemt isNodata het over. Het standaardpredicaat behandelt een lege dataSet (of elke reeks die leeg is) als geen data - je kunt dit overschrijven met een boolean of een functie:

tsx
// boolean shortcut
<LineChart isLoading={query.isPending} isNodata={query.data?.length === 0} noDataLabel="No data available" />

// function predicate
<LineChart isNodata={(ds) => ds.every(s => s.data.length === 0)} />

In React accepteren de props isNodataComponent en isLoadingComponent elke ReactNode. De engine blijft gemount (zodat onChartDataProcessed nog steeds wordt getriggerd); jouw node wordt als overlay boven op de chart-host gerenderd, en suppressDefaultOverlay wordt automatisch ingesteld zodat het ingebouwde .mv-nodata-bericht verborgen blijft:

tsx
<LineChart
  isLoading={isPending}
  isLoadingComponent={<Spinner />}
  isNodata={isEmpty}
  isNodataComponent={<p className="no-data">No results for this selection.</p>}
/>

Voor vanilla JS / andere frameworks wordt de ingebouwde overlay standaard getoond. Onderdruk deze en render je eigen node naast de chart-host:

ts
const chart = mountLineChart(el, { ...props, suppressDefaultOverlay: true });
// render your overlay next to el when data-mv-state === "nodata"

Asconfiguratie

PropStandaardEffect
yTicks10Aantal tick-intervallen op de y-as
showGridLinestrueHorizontale (y) gestreepte rasterlijnen
showVerticalGridLinesfalseVerticale (x) gestreepte rasterlijnen
highlightZeroLinetrueTekent y = 0 als een doorgetrokken lijn

De kleur van de nullijn valt standaard terug op de rasterkleur (--michi-vz-grid). Overschrijf deze onafhankelijk:

css
.my-chart-host {
  --michi-vz-zero-line: #e53935; /* solid red zero line */
  --michi-vz-grid: #e0e0e0;      /* dashed gridlines stay grey */
}
tsx
<LineChart
  yTicks={5}
  showGridLines={true}
  showVerticalGridLines={false}
  highlightZeroLine={true}
/>

Lettertypefamilie

Geef fontFamily door om SVG-labels en canvas-tekst gesynchroniseerd te houden. De engine schrijft --michi-vz-font-family op de chart-host; zowel de SVG-<text>-elementen als het canvas ctx.font-pad lezen die berekende stijl, dus is geen font-embedding vereist - het lettertype hoeft alleen al door de pagina te zijn geladen.

tsx
<LineChart fontFamily="Inter, sans-serif" />
ts
mountLineChart(el, { ...props, fontFamily: "Inter, sans-serif" });

Kleuren en legendagegevens

Lijnkleuren volgen het CSS-contract van data-label-safe. Elk reekselement draagt een data-label-safe-attribuut (het gesaniteerde reekslabel); je richt je hierop in CSS om de streepkleur (stroke) in te stellen. De canvas-renderer meet die berekende stijlen op het moment van renderen, zodat dezelfde CSS-regels beide renderers aansturen.

onChartDataProcessed (en getContext()) geven een legendData-array door op de ChartContext. Elke entry heeft { label, color, order, disabled?, dataLabelSafe }. Een kleurautoriteit (bijvoorbeeld een provider-component) kan deze entries lezen en de bijbehorende CSS genereren:

tsx
<LineChart
  onChartDataProcessed={(ctx) => {
    ctx.legendData?.forEach(({ dataLabelSafe, color }) => {
      // write `.line[data-label-safe="${dataLabelSafe}"] { stroke: ${color} }` into a <style> tag
    });
  }}
/>

API

Props zijn getypeerd als LineChartProps in @michi-vz/core. Gedeeld door alle grafieken: width, height, margin, colors / colorsMapping, renderer ("svg", "canvas", of experimenteel "webgpu"), highlightItems, disabledItems, en de on*-callbacks. onChartDataProcessed / getContext() retourneren de renderer-agnostische ChartContext.