Skip to content

Sankey ​

Stroom

"Waar gaat het allemaal naartoe?" Een Sankey-diagram brengt de stroom door een systeem in kaart: knopen worden ingedeeld in kolommen, en de dikte van elke band is de stroomwaarde. Het is het juiste beeld voor handel tussen exporteurs en markten, budget van bronnen naar bestemmingen, verkeer van verwijzers naar pagina's - overal waar een hoeveelheid onderweg opsplitst en weer samenkomt.

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

De lay-out wordt berekend met d3-sankey: knopen worden op basis van de grafiektopologie aan kolommen toegewezen, verticaal gepakt, en verbindingen worden getekend als vloeiende horizontale banden. Hover over een knoop of een stroom voor de bijbehorende cijfers.

Wanneer kies je deze ​

  • Stromen met structuur. Handel tussen exporteurs en markten, budget van bronnen naar bestemmingen, gebruikers door een trechter: waar hoeveelheden vandaan komen, waar ze naartoe gaan, wat opsplitst en weer samenkomt.
  • Het dominante pad en de lekken vinden. Banddikte is de waarde, dus de dikke routes en de dunne verliezen lees je meteen af - het auditoverzicht van elk systeem.
  • Slechts één fase? Als er niets doorheen stroomt, rangschikt een staafdiagram bronnen overzichtelijker dan een Sankey met twee kolommen.

Nadruk bij hover ​

Hover toont altijd een tooltip. Met hoverHighlight: true zet de grafiek ook naar voren waar je naar wijst: hover over een knoop en die knoop, elke stroom die erin of eruit gaat en de knopen aan de andere uiteinden blijven op volle sterkte terwijl de rest vervaagt; hover over een stroom en alleen die stroom en zijn twee eindknopen blijven opgelicht. Standaard uit.

Example
canvas · responsive
Ga dieper: Insights-gids·DevTools-gids
tsx
<SankeyChart {...props} hoverHighlight />
vue
<SankeyChart :options="{ ...props, hoverHighlight: true }" />
svelte
<div use:sankeyChart={{ ...props, hoverHighlight: true }}></div>
ts
applySankeyChartProps(this.c.nativeElement, { ...props, hoverHighlight: true });
html
<michi-vz-sankey-chart hover-highlight></michi-vz-sankey-chart>
  • De nadruk wordt ter plekke toegepast: svg past de dekking aan van de markeringen die al getekend zijn, canvas en webgpu tekenen opnieuw. Hoveren rendert de grafiek nooit opnieuw, dus je hoeft onHighlightItem niet terug te voeren in highlightItems voor dit effect. onHighlightItem vuurt nog steeds zoals voorheen, voor je eigen UI.
  • Het dimt tot dezelfde niveaus als highlightItems (stromen op een kwart van linkOpacity, knopen op 0,25). Tijdens het hoveren wint het van highlightItems; wie wegbeweegt krijgt de highlightItems-toestand terug.
  • Klik om de tooltip vast te zetten; de nadruk blijft erbij tot de tooltip wordt losgemaakt (klik op de tooltip, of ergens buiten de grafiek).
  • Op de svg-renderer volgt het vervagen enableTransitions; canvas en webgpu schakelen meteen om.

Veel data op WebGPU Experimenteel ​

De optionele renderer="webgpu" van deze grafiek tekent de marks op de GPU, terwijl assen/labels/tooltips op SVG blijven; capability-gated met automatische terugval naar 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 · ~150 links… detecting

Speel door de jaren heen ​

Geef elke link een date en zet timeline aan: de momentopname van een jaar bestaat uit de links die die datum delen - knopen worden gedeeld over de jaren heen, alleen de stromen komen en gaan - en de banddikte glijdt tussen jaren. Standaard uit - zonder opt-in verandert er niets. Dit is interactief jaar-voor-jaar stappen, niet de eenmalige intro verderop.

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

<SankeyChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / seekIndex(i) / stepForward()
vue
<SankeyChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" />
svelte
<div use:sankeyChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>
ts
applySankeyChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });
html
<michi-vz-sankey-chart id="c"></michi-vz-sankey-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.
  • Links zonder date 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<SankeyChartHandle>(null);

<SankeyChart
  ref={ref}
  {...props}
  progressiveDraw={{ durationMs: 2000 }}
/>;
// ref.current?.replay() speelt de animatie opnieuw af
vue
<SankeyChart :options="{ ...props, progressiveDraw: { durationMs: 2000 } }" />
svelte
<div use:sankeyChart={{ ...props, progressiveDraw: { durationMs: 2000 } }}></div>
ts
applySankeyChartProps(this.c.nativeElement, {
  ...props,
  progressiveDraw: { durationMs: 2000 },
});
html
<michi-vz-sankey-chart id="c"></michi-vz-sankey-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.

Gebruik ​

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

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

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

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

// component (uses CUSTOM_ELEMENTS_SCHEMA)
// template: <michi-vz-sankey-chart #c></michi-vz-sankey-chart>
applySankeyChartProps(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-sankey-chart id="c"></michi-vz-sankey-chart>
<script>
  Object.assign(document.getElementById("c"), props); // nodes, links, …
</script>
ts
import { mountSankeyChart } from "@michi-vz/core";

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

Datavorm ​

In tegenstelling tot de andere grafieken neemt een Sankey twee arrays aan: nodes (elk met een unieke id, een optionele label en color) en links (source → target op basis van id, met een value).

ts
const props = {
  linkColorMode: "source", // colour links by their source (or "target")
  nodes: [
    { id: "France" }, { id: "Germany" },
    { id: "EU" }, { id: "Asia" },
  ],
  links: [
    { source: "France", target: "EU", value: 40 },
    { source: "France", target: "Asia", value: 22 },
    { source: "Germany", target: "EU", value: 55 },
    { source: "Germany", target: "Asia", value: 35 },
  ],
};

Een link naar een onbekende knoop-id (of een knoop in disabledItems) wordt met een datawarning weggelaten; het uitschakelen van een knoop laat ook de bijbehorende links vervallen.

Lay-outinstellingen ​

nodeWidth bepaalt de breedte van het knooprechthoek, nodePadding de verticale tussenruimte tussen knopen in een kolom, en linkOpacity hoe doorschijnend de banden zijn. linkColorMode kleurt elke band naar zijn source-knoop (standaard) of target-knoop. De a11y-spiegel en getContext() presenteren de links als een leesbare "Bron → Doel: waarde"-tabel.

Afgeronde knopen. nodeRadius (px, standaard 2) rondt de hoeken van het knooprechthoek af - verhoog de waarde voor het pilvormige uiterlijk, of zet hem op 0 voor rechte hoeken. Deze wordt begrensd tot de helft van de kortste zijde van de knoop, zodat een dunne knoop nooit vervormt.

Afgeronde stromen. De stromen worden getekend als gevulde linten; linkRadius (px, standaard 2) rondt hun hoeken af waar ze de knopen raken, voor een zachtere verbinding (begrensd tot de helft van de banddikte; 0 = scherp). linkColorMode kleurt elke stroom naar zijn source- of target-knoop, op linkOpacity:

ts
const props = { nodeRadius: 4, linkRadius: 4, /* …nodes, links */ };

Volgorde van knopen. Standaard ordent d3-sankey de knopen in een kolom om kruisingen te beperken. nodeSort: "input" houdt de volgorde van je nodes-array aan (en de volgorde van links voor de banden die een knoop verlaten), zodat een knoop "Rest van de wereld" of "Overige" blijft staan waar je hem hebt gezet, meestal onderaan.

Dunne stromen. Een straaltje naast een grote stroom kan een band van één pixel zijn, onmogelijk te hoveren. minLinkValue (in waarde-eenheden) legt zulke links uit alsof ze minstens die waarde hadden; alleen de geometrie verandert, de tooltip, de context en de totalen houden de echte waarden. Kies hem uit je data, bijvoorbeeld 15% van de grootste link.

Klikken. onNodeClick(node, event) en onLinkClick(link, event) vuren bij een klik op een knoop of een stroom, in svg en via de canvas-hittest, met dezelfde context die de tooltip-formatter krijgt. De klik pint de tooltip nog steeds; gebruik de callbacks om vanaf de markering zelf te navigeren of te selecteren.

ts
const props = { nodeSort: "input", minLinkValue: 0.15 * maxLinkValue, onNodeClick: (n) => go(n.id) };

API ​

Props zijn getypeerd als SankeyChartProps 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. Volledige referentie: Sankey API.