Sankey
Flux
« Où tout cela va-t-il ? » Un diagramme de Sankey retrace le flux à travers un système : les nœuds sont disposés en colonnes, et l'épaisseur de chaque bande représente la valeur du flux. C'est l'image qu'il faut pour le commerce entre exportateurs et marchés, le budget des sources vers les usages, le trafic des référents vers les pages - partout où une quantité se divise et se recombine sur son parcours.
La disposition est calculée avec d3-sankey : les nœuds sont assignés à des colonnes à partir de la topologie du graphe, empilés verticalement, et les liens sont dessinés comme des bandes horizontales lissées. Survolez un nœud ou un flux pour voir ses chiffres.
Quand le choisir
- Des flux structurés. Commerce entre exportateurs et marchés, budget des sources vers les usages, utilisateurs à travers un entonnoir : d'où viennent les quantités, où elles vont, ce qui se divise et se recombine.
- Repérer le chemin dominant et les fuites. L'épaisseur des bandes est la valeur, si bien que les grandes routes et les petites pertes se lisent instantanément - la vue d'audit de n'importe quel système.
- Une seule étape ? Si rien ne traverse à travers, une barre comparable classe les sources plus proprement qu'un Sankey à deux colonnes.
Mise en avant au survol
Le survol affiche toujours une infobulle. Avec hoverHighlight: true, le graphique met aussi en avant ce que vous pointez : survolez un nœud, et ce nœud, chaque flux qui y entre ou en sort et les nœuds à l'autre extrémité restent pleinement visibles tandis que le reste s'estompe ; survolez un flux, et seuls ce flux et ses deux nœuds d'extrémité restent allumés. Désactivé par défaut.
<SankeyChart {...props} hoverHighlight /><SankeyChart :options="{ ...props, hoverHighlight: true }" /><div use:sankeyChart={{ ...props, hoverHighlight: true }}></div>applySankeyChartProps(this.c.nativeElement, { ...props, hoverHighlight: true });<michi-vz-sankey-chart hover-highlight></michi-vz-sankey-chart>- La mise en avant s'applique sur place : en svg, l'opacité des marques déjà dessinées change ; canvas et webgpu redessinent. Le survol ne redessine jamais le graphique, il est donc inutile de renvoyer
onHighlightItemdanshighlightItemspour obtenir cet effet.onHighlightItemse déclenche toujours comme avant, pour votre propre interface. - Elle atténue aux mêmes niveaux que
highlightItems(flux au quart delinkOpacity, nœuds à 0,25). Pendant le survol, elle l'emporte surhighlightItems; en vous éloignant, l'étathighlightItemsrevient. - Cliquez pour épingler l'infobulle : la mise en avant reste avec elle jusqu'à ce que l'épingle soit levée (clic sur l'infobulle, ou n'importe où hors du graphique).
- En svg, le fondu suit
enableTransitions; canvas et webgpu basculent immédiatement.
Données volumineuses sur WebGPU Expérimental
Le renderer="webgpu" optionnel de ce graphique dessine ses marques sur le GPU tandis que les axes/libellés/infobulles restent sur SVG ; conditionné aux capacités du navigateur, avec repli automatique sur canvas.
Faire defiler les annees
Étiquetez chaque lien avec une date et activez timeline : l'instantané d'une année est constitué des liens partageant cette date - les nœuds restent partagés d'une année à l'autre, seuls les flux vont et viennent - et l'épaisseur des bandes glisse d'une année à l'autre. Désactivé par défaut - sans opt-in, rien ne change. C'est un défilement interactif année par année, pas l'entrée ponctuelle plus bas.
Appuyez sur le bouton lecture sous le graphique : les données défilent année par année, un instantané à la fois. Faites glisser le curseur pour sauter à une année.
const ref = useRef<SankeyChartHandle>(null);
<SankeyChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / seekIndex(i) / stepForward()<SankeyChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" /><div use:sankeyChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>applySankeyChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });<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>speedMsrègle le rythme,loopreboucle,autoplay: truedémarre au montage,showControl: falsemasque la barre intégrée.- Les valeurs glissent d'une période à l'autre par défaut (
interpolate) ; ajustez le mouvement avectweenMseteasing, ou passezinterpolate: falsepour des coupes nettes. Avec reduced motion, la coupe est toujours nette. - Le contrôleur headless reste disponible :
chart.timeline()exposeplay() / pause() / toggle() / seek(period) / seekIndex(i) / stepForward() / stepBack(), plusonStepetformatPerioddans la config pour une UI maison. seek(period)cherche d'abord la période, en comparant le texte :seek(2021)etseek("2021")vont tous les deux sur 2021, que vos dates soient des nombres ou des chaînes. Un nombre ne compte comme une position (0 = la première) que si aucune période ne correspond, et une chaîne qui ne correspond à rien ne fait rien.seekIndex(i)va toujours par position, comme le curseur intégré.- Les liens sans
daterestent visibles à chaque période. timelinel'emporte surprogressiveDrawquand les deux sont définis - l'animation de révélation plus bas reste inactive tant que le timeline garde la main.
Animation de révélation
Le graphique se dessine de gauche à droite au montage, révélant ses éléments dans l'ordre avant de se stabiliser. Désactivé par défaut - un graphique l'active avec la prop progressiveDraw.
Chaque ligne grandit de la première à la dernière année ; l'étiquette suit la pointe puis se pose à l'extrémité de la ligne. Avec reduced motion activé, le graphique s'affiche entièrement tracé, instantanément.
progressiveDraw: true applique les réglages par défaut (1200 ms, easeInOutCubic). Un objet de configuration affine le comportement :
const ref = useRef<SankeyChartHandle>(null);
<SankeyChart
ref={ref}
{...props}
progressiveDraw={{ durationMs: 2000 }}
/>;
// ref.current?.replay() rejoue l'animation à la demande<SankeyChart :options="{ ...props, progressiveDraw: { durationMs: 2000 } }" /><div use:sankeyChart={{ ...props, progressiveDraw: { durationMs: 2000 } }}></div>applySankeyChartProps(this.c.nativeElement, {
...props,
progressiveDraw: { durationMs: 2000 },
});<michi-vz-sankey-chart id="c"></michi-vz-sankey-chart>
<script>
const el = document.getElementById("c");
el.progressiveDraw = { durationMs: 2000 };
// el.replay() rejoue l'animation
</script>durationMseteasing("linear", "easeOutQuad", "easeInOutCubic", ou une fonction(t) => tpersonnalisée) façonnent le rythme du tracé.autoplay: falserend le graphique entièrement tracé ; appelezreplay()(handle de ref React, méthode du web component ou instance core) pour lancer l'animation à la demande.replayOnUpdate: truela rejoue à chaque changement de données.- Respecte
prefers-reduced-motion: le graphique s'affiche alors entièrement tracé, instantanément. - L'animation de révélation est une entrée ponctuelle ; le défilement par année ci-dessus avance plutôt année par année.
Utilisation
import { SankeyChart } from "@michi-vz/react";
export default () => <SankeyChart {...props} />; // props = the chart options<script setup>
import { SankeyChart } from "@michi-vz/vue";
</script>
<template>
<SankeyChart :options="props" />
</template><script>
import { sankeyChart } from "@michi-vz/svelte";
</script>
<div use:sankeyChart={props}></div>// 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);<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>import { mountSankeyChart } from "@michi-vz/core";
const chart = mountSankeyChart(el, props);
chart.update(next);
chart.getContext(); // renderer-agnostic, LLM-ready
chart.destroy();Forme des données
Contrairement aux autres graphiques, un Sankey prend deux tableaux : nodes (chacun avec un id unique, un label et une color optionnels) et links (source → target par id, avec une value).
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 },
],
};Un lien vers un id de nœud inconnu (ou un nœud dans disabledItems) est supprimé avec un datawarning ; désactiver un nœud supprime aussi ses liens.
Réglages de disposition
nodeWidth définit la largeur du rectangle du nœud, nodePadding l'écart vertical entre les nœuds d'une colonne, et linkOpacity la translucidité des bandes. linkColorMode colore chaque bande selon son nœud source (par défaut) ou target. Le miroir d'accessibilité et getContext() exposent les liens sous forme de tableau lisible « Source → Target : valeur ».
Nœuds arrondis. nodeRadius (px, par défaut 2) arrondit les coins du rectangle du nœud - augmentez-le pour un aspect « pilule », ou mettez 0 pour des coins carrés. Il est plafonné à la moitié du côté le plus court du nœud, afin de ne jamais déformer un nœud fin.
Flux arrondis. Les flux sont dessinés comme des rubans remplis ; linkRadius (px, par défaut 2) arrondit leurs coins à la jonction avec les nœuds, pour une connexion plus douce (plafonné à la moitié de l'épaisseur de la bande ; 0 = coins nets). linkColorMode colore chaque flux selon son nœud source ou target, à l'opacité linkOpacity :
const props = { nodeRadius: 4, linkRadius: 4, /* …nodes, links */ };Ordre des nœuds. Par défaut, d3-sankey range les nœuds d'une colonne pour réduire les croisements. nodeSort: "input" garde l'ordre de votre tableau nodes (et l'ordre des links pour les bandes qui partent d'un nœud), de sorte qu'un nœud « Reste du monde » ou « Autres » reste là où vous l'avez listé, en général en dernier.
Flux fins. Un filet à côté d'un gros flux peut n'être qu'une bande d'un pixel, impossible à survoler. minLinkValue (en unités de valeur) dispose ces liens comme s'ils avaient au moins cette valeur ; seule la géométrie change, l'infobulle, le contexte et les totaux gardent les vraies valeurs. Choisissez-le d'après vos données, par exemple 15 % du plus grand lien.
Clics. onNodeClick(node, event) et onLinkClick(link, event) se déclenchent au clic sur un nœud ou un flux, en svg comme via le test de collision du canvas, avec le même contexte que reçoit le formateur d'infobulle. Le clic épingle toujours l'infobulle ; utilisez les callbacks pour naviguer ou sélectionner depuis la marque elle-même.
const props = { nodeSort: "input", minLinkValue: 0.15 * maxLinkValue, onNodeClick: (n) => go(n.id) };API
Les props sont typées comme SankeyChartProps dans @michi-vz/core. Communes à tous les graphiques : width, height, margin, colors / colorsMapping, renderer ("svg", "canvas", ou "webgpu" expérimental), highlightItems, disabledItems, et les callbacks on*. onChartDataProcessed / getContext() renvoient le ChartContext indépendant du moteur de rendu. Référence complète : API Sankey.
