Barres comparables
Comparaison
Est-ce que ça s'est amélioré ou dégradé ? Placez avant et après côte à côte sur une barre par étiquette, et l'écart qui s'est réduit (ou creusé) est la première chose que voit le lecteur.
- 2019 (avant, teinte pâle)
- 2024 (après, plein)
Le graphique ci-dessus est le même moteur dans chaque framework - seul le code d'intégration ci-dessous diffère.
Données volumineuses sur WebGPU Expérimental
ComparableHorizontalBarChart dispose d'un renderer="webgpu" optionnel qui peint les deux sous-barres par ligne comme des rectangles instanciés sur le GPU tandis que les axes, étiquettes et infobulles restent sur la couche SVG. C'est conditionné par les capacités du navigateur : sur un navigateur sans WebGPU, il rétrograde automatiquement vers canvas, et getContext().renderer indique lequel a effectivement peint.
Faire defiler les annees
Donnez un date à chaque ligne et activez timeline : le graphique en barres devient un récit année par année, avec son propre bouton lecture et son curseur, un instantané de l'écart avant/après à la fois. Désactivé par défaut - sans opt-in, rien ne change.
Appuyez sur le bouton lecture sous le graphique : les données défilent année après année, un instantané à la fois. Faites glisser le curseur pour sauter à une année.
const ref = useRef<ComparableHorizontalBarChartHandle>(null);
<ComparableHorizontalBarChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / seekIndex(i) / stepForward()<ComparableHorizontalBarChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" /><div use:comparableHorizontalBarChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>applyComparableHorizontalBarChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });<michi-vz-comparable-horizontal-bar-chart id="c"></michi-vz-comparable-horizontal-bar-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é.- Un
filter(top-N, tri) s'applique toujours à l'intérieur de chaque période : un « top 5 par an » fonctionne d'emblée. - Les lignes sans
daterestent visibles à chaque période.
Rayon des coins
Chaque barre est dessinée avec des coins de 5px par défaut. Sur un petit écran, les barres s'amincissent - les demi-bandes de la mise en page grouped peuvent ne faire qu'environ 10px d'épaisseur - et un rayon de 5px sur une barre de 10px en fait une pilule. barRadius fixe le rayon en px : 0 pour des coins carrés, de 2 à 4 pour des coins presque carrés.
barRadius: 5 (par défaut)barRadius: 2barRadius: 0Même graphique, mêmes données ; seul barRadius change. Changez de moteur de rendu : svg et canvas arrondissent les coins de façon identique.
<ComparableHorizontalBarChart {...props} layout="grouped" barRadius={2} /><ComparableHorizontalBarChart :options="{ ...props, layout: 'grouped', barRadius: 2 }" /><div use:comparableHorizontalBarChart={{ ...props, layout: "grouped", barRadius: 2 }}></div>applyComparableHorizontalBarChartProps(this.c.nativeElement, { ...props, layout: "grouped", barRadius: 2 });<michi-vz-comparable-horizontal-bar-chart layout="grouped" bar-radius="2"></michi-vz-comparable-horizontal-bar-chart>- Le rayon dessiné ne dépasse jamais la moitié de la largeur ou de la hauteur de la barre, en svg comme en canvas.
- Sans
barRadius, l'aspect historique de 5px est conservé. Une valeur négative donne des coins carrés. - Le moteur de rendu expérimental
webgpudessine des coins carrés, quelle que soit la valeur.
Usage
import { ComparableHorizontalBarChart } from "@michi-vz/react";
export default () => <ComparableHorizontalBarChart {...props} />; // props = the chart options<script setup>
import { ComparableHorizontalBarChart } from "@michi-vz/vue";
</script>
<template>
<ComparableHorizontalBarChart :options="props" />
</template><script>
import { comparableHorizontalBarChart } from "@michi-vz/svelte";
</script>
<div use:comparableHorizontalBarChart={props}></div>// main.ts - register the elements once
import "@michi-vz/angular";
import { applyComparableHorizontalBarChartProps } from "@michi-vz/angular";
// component (uses CUSTOM_ELEMENTS_SCHEMA)
// template: <michi-vz-comparable-horizontal-bar-chart #c></michi-vz-comparable-horizontal-bar-chart>
applyComparableHorizontalBarChartProps(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-comparable-horizontal-bar-chart id="c"></michi-vz-comparable-horizontal-bar-chart>
<script>
Object.assign(document.getElementById("c"), props); // dataSet/series, title, …
</script>import { mountComparableHorizontalBarChart } from "@michi-vz/core";
const chart = mountComparableHorizontalBarChart(el, props);
chart.update(next);
chart.getContext(); // renderer-agnostic, LLM-ready
chart.destroy();API
Les props sont typées comme ComparableHorizontalBarChartProps dans @michi-vz/core. Communes à tous les graphiques : width, height, margin, colors / colorsMapping, renderer ("svg", "canvas", ou expérimentalement "webgpu"), highlightItems, disabledItems, et les callbacks on*. onChartDataProcessed / getContext() renvoient le ChartContext agnostique du moteur de rendu.
Notes de comportement
Deux sous-barres par ligne
Chaque ligne dessine valueBased (pâle) et valueCompared (plein), divergeant depuis x=0 ; la sous-barre la plus courte est dessinée au-dessus, si bien que les deux valeurs restent visibles quel que soit le sens de l'évolution. colorsBasedMapping donne à la sous-barre valueBased sa propre couleur par étiquette : associez une teinte claire opaque à valueBasedOpacity: 1 (comme dans la démo ci-dessus) pour le contraste avant/après le plus net dans les deux thèmes. valueBasedOpacity / valueComparedOpacity définissent leur opacité de remplissage. Une sous-barre dont le remplissage résolu est transparent est ignorée (les consommateurs cachent une moitié via CSS). minBarWidth (5 par défaut) impose un plancher à une barre non nulle pour que les valeurs proches de zéro restent visibles.
patternsMapping - remplissages hachurés / image
patternsMapping: Record<label, imageSrc> remplit la sous-barre valeur de base avec une image en mosaïque au lieu d'une couleur unie. createHatchPattern({ color, angle?, spacing?, strokeWidth? }) (exporté depuis @michi-vz/core et @michi-vz/react) renvoie une URI de données SVG à hachures diagonales pour le cas courant. Le moteur de rendu canvas la met en mosaïque via ctx.createPattern et effectue un nouveau rendu une fois l'image chargée.
Axe des valeurs (x)
xAxisPredefinedDomain: [min, max] fixe la plage de l'axe des valeurs (alias de xAxisDomain). showZeroLineForXAxis dessine une ligne pleine à x=0 (graphiques divergents) ; showGrid bascule les lignes de grille verticales (désactivées par défaut). xAxisFormat formate les étiquettes des graduations.
Colonne d'étiquettes (y)
Les étiquettes de catégorie de l'axe des y vivent dans une colonne gauche de tickHtmlWidth px de large (100 par défaut, avec points de suspension). padding.left décale la zone de tracé (barres + axe des valeurs) vers la droite SANS déplacer les étiquettes - ouvrant de la place pour une colonne d'étiquettes large. horizontalTickPosition: { x, y } déplace légèrement les étiquettes pour les aligner avec une légende externe. hideTickLabels les masque entièrement (quand les noms de catégorie vivent plutôt dans une légende).
Infobulle
tooltipFormatter(datum, dataSet, type) reçoit la ligne survolée, toutes les lignes, et le type de sous-barre survolée ("based" | "compared"). Elle renvoie une chaîne HTML ; le wrapper React accepte en plus un nœud React (converti en HTML statique). L'infobulle intégrée est consciente des bords (elle bascule près des bords droit/haut).
Chargement / absence de données + interaction
isLoading et isNodata pilotent la superposition (React : isLoadingComponent / isNodataComponent). Survoler met en évidence une ligne (les autres s'estompent) et mouseleave la réinitialise ; les barres sont arrondies (rayon 5 par défaut, réglé par barRadius) avec une bordure de 1px.
Autorités de couleur côté consommateur : le contexte porte
legendData({ label, color, dataLabelSafe }) pour qu'un système de couleur par injection CSS puisse indexer des règles par étiquette ;onChartDataProcessedn'est émis que lorsque le contexte change (réémettre un contexte inchangé à chaque rendu peut boucler un consommateur qui déclenche une action à chaque appel).
