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.
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.
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:
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.
<LineChart
{...props}
xAxisDataType="date_monthly"
fillPeriodTicks
noDataTickTooltip={() => "No data reported for this month"}
noDataTickColor="#c0392b"
/><LineChart :options="{ ...props, fillPeriodTicks: true, noDataTickTooltip: () => 'No data' }" /><div use:lineChart={{ ...props, fillPeriodTicks: true, noDataTickTooltip: () => 'No data' }}></div>applyLineChartProps(this.c.nativeElement, {
...props,
fillPeriodTicks: true,
noDataTickTooltip: () => "No data",
});<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.
const ref = useRef<LineChartHandle>(null);
<LineChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / stepForward()<LineChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" /><div use:lineChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>applyLineChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });<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>speedMsbepaalt het tempo,loopbegint opnieuw,autoplay: truestart bij mounten,showControl: falseverbergt de ingebouwde balk.- De headless controller is altijd beschikbaar:
chart.timeline()biedtplay() / pause() / toggle() / seek(period) / stepForward() / stepBack(), plusonStepenformatPeriodin de config voor eigen UI. - Waarden glijden standaard tussen jaren (
interpolate); zetinterpolate: falsevoor harde overgangen. Met reduced motion is de overgang altijd hard. timelinewint het vanprogressiveDrawwanneer beide op dezelfde grafiek staan.tipLabel: truein detimeline-config laat een label meebewegen met de groeiende lijnpunt tijdens het afspelen - hetzelfde tiplabel als bijprogressiveDraw, 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:
const ref = useRef<LineChartHandle>(null);
<LineChart
ref={ref}
{...props}
progressiveDraw={{ durationMs: 2000, tipLabel: true }}
/>;
// ref.current?.replay() speelt de animatie opnieuw af<LineChart :options="{ ...props, progressiveDraw: { durationMs: 2000, tipLabel: true } }" /><div use:lineChart={{ ...props, progressiveDraw: { durationMs: 2000, tipLabel: true } }}></div>applyLineChartProps(this.c.nativeElement, {
...props,
progressiveDraw: { durationMs: 2000, tipLabel: true },
});<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>durationMseneasing("linear", "easeOutQuad", "easeInOutCubic", of een eigen(t) => t-functie) bepalen het tempo.tipLabel: truetekent 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: falserendert de grafiek volledig getekend; roepreplay()aan (React-ref-handle, webcomponent-methode of de core-instantie) om de animatie op aanvraag te starten.replayOnUpdate: trueherhaalt 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
import { LineChart } from "@michi-vz/react";
export default () => <LineChart {...props} />; // props = the chart options<script setup>
import { LineChart } from "@michi-vz/vue";
</script>
<template>
<LineChart :options="props" />
</template><script>
import { lineChart } from "@michi-vz/svelte";
</script>
<div use:lineChart={props}></div>// 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);<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>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:
// 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:
<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:
const chart = mountLineChart(el, { ...props, suppressDefaultOverlay: true });
// render your overlay next to el when data-mv-state === "nodata"Asconfiguratie
| Prop | Standaard | Effect |
|---|---|---|
yTicks | 10 | Aantal tick-intervallen op de y-as |
showGridLines | true | Horizontale (y) gestreepte rasterlijnen |
showVerticalGridLines | false | Verticale (x) gestreepte rasterlijnen |
highlightZeroLine | true | Tekent y = 0 als een doorgetrokken lijn |
De kleur van de nullijn valt standaard terug op de rasterkleur (--michi-vz-grid). Overschrijf deze onafhankelijk:
.my-chart-host {
--michi-vz-zero-line: #e53935; /* solid red zero line */
--michi-vz-grid: #e0e0e0; /* dashed gridlines stay grey */
}<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.
<LineChart fontFamily="Inter, sans-serif" />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:
<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.
