Skip to content

Radardiagram ​

Vergelijking

Welke optie wint, en waar? Leg een paar kandidaten over dezelfde set criteria en elk wordt een veelhoek die je in één oogopslag kunt lezen - de pieken tonen elke sterkte, de dalen tonen elke zwakte, en de overlappingen tonen precies waar ze van plaats wisselen.

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 ​

  • Scorekaarten. Leveranciersevaluaties, kandidaatbeoordelingen, productbenchmarks: een paar opties over dezelfde criteria, elk een veelhoek waarvan de pieken en dalen de sterktes en zwaktes zijn.
  • Balans versus specialisatie. Een rondere veelhoek is de allrounder; een puntige zet alles op twee assen. Dat vormverhaal is wat tabellen niet kunnen vertellen.
  • Houd het bij een paar entiteiten en 5 tot 12 assen. Voor een precieze vergelijking op één criterium leest een staafdiagram de exacte waarden af; de radar leest profielen.

De buitenste ring afronden naar een mooie waarde ​

Zonder maxValue ligt de buitenste ring op de grootste waarde in de data, dus een maximum van 73 labelt de ringen 18, 37, 55 en 73. Zet niceMaxValue aan en de buitenste ring rondt in plaats daarvan naar boven af tot de volgende "mooie" tick, op dezelfde stappen van 1, 2 en 5 × 10ⁿ die een d3-as gebruikt, zodat de ringen 20, 40, 60 en 80 tonen.

Example
canvas · responsive
Ga dieper: Insights-gids·DevTools-gids
tsx
<RadarChart {...props} niceMaxValue />
vue
<RadarChart :options="{ ...props, niceMaxValue: true }" />
svelte
<div use:radarChart={{ ...props, niceMaxValue: true }}></div>
ts
applyRadarChartProps(this.c.nativeElement, { ...props, niceMaxValue: true });
html
<michi-vz-radar-chart id="c"></michi-vz-radar-chart>
<script>
  // een property, geen attribuut: accepteert true of een aantal ticks
  document.getElementById("c").niceMaxValue = true;
</script>
  • true gebruikt rings (standaard 4) als aantal ticks. Een getal bepaalt het aantal ticks zelf, bijvoorbeeld niceMaxValue: 5 om aan te sluiten bij een as met vijf ticks elders op de pagina.
  • Een expliciete maxValue wint altijd: niceMaxValue rondt alleen de buitenste ring af die de grafiek anders uit de data zou halen. Een maximum dat al op een tick ligt blijft staan (100 blijft 100), en data die volledig nul of negatief is houdt de standaard buitenste ring van 1.
  • Alleen de buitenste ring is gegarandeerd rond. De binnenste ringen zijn gelijke delen ervan, dus ze zijn rond wanneer het afgeronde maximum deelbaar is door rings. 80 over 4 ringen geeft 20, 40, 60, 80, terwijl 50 over 4 ringen 12,5, 25, 37,5, 50 geeft.
  • getContext().maxValue geeft de afgeronde waarde terug. De afronding is ook geëxporteerd als pure helper, niceRadarMax(dataMax, tickCount) uit @michi-vz/core.

Veel data op WebGPU Experimenteel ​

RadarChart heeft een optionele renderer="webgpu" die de veelhoekvullingen en polmarkers tekent als GPU-instanced marks, terwijl assen, labels en tooltips op de SVG-laag blijven. Dit is capability-gated: in een browser zonder WebGPU wordt automatisch teruggevallen op canvas, en getContext().renderer rapporteert wat daadwerkelijk is getekend.

⚗️ 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 · 12 axes × 4 series… detecting

Speel door de jaren heen ​

RadarChart gebruikt date al voor de legacy vorm per as { date, value }, dus de timeline-tag heet in plaats daarvan period. Geef elke serie-rij een period en zet timeline aan: de momentopname van een jaar bestaat uit de rijen die die period delen, en elke veelhoek glijdt tussen jaren. Standaard uit - zonder opt-in verandert er niets. Dit is interactief jaar-voor-jaar stappen, niet de eenmalige intro verderop.

ts
{ label: "Vienna", period: "2021", values: [72, 65, 40, 88 /* … */] }

Druk op de afspeelknop onder de grafiek: de data stapt door de jaren, één momentopname per keer. Sleep de scrubber om naar een jaar te springen.

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

<RadarChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / seekIndex(i) / stepForward()
vue
<RadarChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" />
svelte
<div use:radarChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>
ts
applyRadarChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });
html
<michi-vz-radar-chart id="c"></michi-vz-radar-chart>
<script>
  const el = document.getElementById("c");
  el.timeline = { speedMs: 1000, loop: true };
  // el.getTimeline() -> play() / pause() / seek(year) / seekIndex(i)
</script>
  • speedMs bepaalt het tempo, loop begint opnieuw, autoplay: true start bij mounten, showControl: false verbergt de ingebouwde balk.
  • Waarden glijden standaard tussen periodes (interpolate); stem de beweging af met tweenMs en easing, of zet interpolate: false voor harde overgangen. Met reduced motion is de overgang altijd hard.
  • De headless controller is altijd beschikbaar: chart.timeline() biedt play() / pause() / toggle() / seek(period) / seekIndex(i) / stepForward() / stepBack(), plus onStep en formatPeriod in de config voor eigen UI.
  • seek(period) zoekt eerst de periode en vergelijkt als tekst, dus seek(2021) en seek("2021") komen allebei op 2021 uit, of je datums nu getallen of strings zijn. Een getal telt alleen als positie (0 = de eerste) als geen periode past, en een string die nergens bij past doet niets. seekIndex(i) gaat altijd op positie, net als de ingebouwde scrubber.
  • Series zonder period blijven in elke periode zichtbaar.
  • timeline wint het van progressiveDraw als beide zijn ingesteld - de onthulanimatie verderop blijft uit zolang de timeline de regie heeft.

Onthulanimatie ​

De grafiek tekent zichzelf van links naar rechts bij het mounten, waarbij de elementen na elkaar verschijnen voordat ze op hun plek vallen. Standaard uit - een grafiek kiest ervoor met de progressiveDraw-prop.

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<RadarChartHandle>(null);

<RadarChart
  ref={ref}
  {...props}
  progressiveDraw={{ durationMs: 2000 }}
/>;
// ref.current?.replay() speelt de animatie opnieuw af
vue
<RadarChart :options="{ ...props, progressiveDraw: { durationMs: 2000 } }" />
svelte
<div use:radarChart={{ ...props, progressiveDraw: { durationMs: 2000 } }}></div>
ts
applyRadarChartProps(this.c.nativeElement, {
  ...props,
  progressiveDraw: { durationMs: 2000 },
});
html
<michi-vz-radar-chart id="c"></michi-vz-radar-chart>
<script>
  const el = document.getElementById("c");
  el.progressiveDraw = { durationMs: 2000 };
  // el.replay() speelt de animatie opnieuw af
</script>
  • durationMs en easing ("linear", "easeOutQuad", "easeInOutCubic", of een eigen (t) => t-functie) bepalen het tempo.
  • 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.
  • Onthulanimatie is een eenmalige animatie bij mounten; speel door de jaren hierboven stapt in plaats daarvan jaar voor jaar door de data.

Hover en tooltips ​

Hoveren over een pool roept tooltipFormatter aan met het datapunt van de pool plus date - het aslabel van de gehoverde pool - en dat is nu consistent op elke renderer (svg, canvas, webgpu):

ts
tooltipFormatter: (item) => `${item.date}: ${item.value}`,

De standaard tooltip-markup (zonder tooltipFormatter) blijft ongewijzigd. Hover volgt de cursor terwijl die over een veelhoek beweegt, in plaats van eenmalig te vuren bij binnenkomst, en highlightItems dempt elke andere serie - gedempte series worden niet gehittest, dus alleen de actieve serie reageert op hover, net als bij canvas en webgpu. disabledItems is een ander mechanisme: het verwijdert een serie volledig uit de grafiek in plaats van hem te dempen, dus een uitgeschakelde serie zat nooit in het rendermodel en kon sowieso niet gehittest worden. Klikken pint de tooltip nu alleen nog als de klik een echte pool oplevert; een klik die niets raakt (lege ruimte, of een gedempte serie, die niet gehittest wordt) pint niet meer, terwijl voorheen op svg een klik ergens op een veelhoek - gedempt inbegrepen - de tooltip altijd pinde.

Op svg vuurt onHighlightItem nu bij elke mousemove die een hit oplevert, en met [] bij elke beweging die dat niet doet - zonder deduplicatie of throttling. Als jouw onHighlightItem-handler het gehoverde label wegschrijft naar een store of andere state die terugkomt in de props van de grafiek, moet je zelf debouncen of dedupliceren, want elke dispatch die de props bereikt triggert een re-render. Dit is niets nieuws voor de bibliotheek: canvas en webgpu vuurden al in dit tempo bij radar-hover, en andere grafieken ook (bijv. Area, Pie) - svg-radar-hover sluit hier nu gewoon bij aan.

Gebruik ​

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

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

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

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

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

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

API ​

Props zijn getypeerd als RadarChartProps 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.