Skip to content

Vergelijkbare verticale staven ​

Vergelijking

Werd het beter of slechter, categorie voor categorie? Elke kolom overlapt twee balken op volledige breedte - de referentiewaarde erachter, de huidige waarde ervoor - met een verschilpijl + label die de verschuiving in één oogopslag toont.

Example
  • 2019 (basis, lichte tint, achter)
  • 2024 (vergeleken, vol, voor)
canvas · responsive
Ga dieper: Insights-gids·DevTools-gids

De grafiek hierboven gebruikt in elk framework dezelfde engine - alleen de integratiecode hieronder verschilt.

Zware datasets op WebGPU Experimenteel ​

ComparableVerticalBarChart heeft een optionele renderer="webgpu" die de twee subbalken per kolom tekent als GPU-instanced rechthoeken, terwijl assen, labels en de verschilindicator op de SVG-laag blijven. Dit is capability-gated: in een browser zonder WebGPU schakelt het automatisch terug naar canvas, en getContext().renderer meldt welke renderer daadwerkelijk heeft 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 · ~60 columns… detecting

Speel door de jaren heen ​

Geef elke categorie een date en zet timeline aan: het kolomdiagram wordt een verhaal per jaar, met een eigen afspeelknop en scrubber die telkens het basis/vergeleken-paar van één periode toont. Standaard uit - zonder opt-in verandert er niets.

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

<ComparableVerticalBarChart ref={ref} {...props} timeline={{ speedMs: 1000, loop: true }} />;
// ref.current?.timeline() -> play() / pause() / seek(year) / seekIndex(i) / stepForward()
vue
<ComparableVerticalBarChart :options="{ ...props, timeline: { speedMs: 1000, loop: true } }" />
svelte
<div use:comparableVerticalBarChart={{ ...props, timeline: { speedMs: 1000, loop: true } }}></div>
ts
applyComparableVerticalBarChartProps(this.c.nativeElement, { ...props, timeline: { speedMs: 1000, loop: true } });
html
<michi-vz-comparable-vertical-bar-chart id="c"></michi-vz-comparable-vertical-bar-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.
  • Een filter (top-N, sortering) blijft binnen elke periode gelden, dus een "top 5 per jaar" werkt meteen.
  • Kolommen zonder date blijven in elke periode zichtbaar.

Hoekradius ​

Elke kolom krijgt standaard hoeken van 5px. Op een klein scherm met veel categorieën worden de kolommen smal - een paar px per kolom - en een radius van 5px maakt er pillen van. barRadius stelt de radius in px in: 0 voor vierkante hoeken, 2-4 voor bijna vierkante.

barRadius: 5 (standaard)
barRadius: 2
barRadius: 0

Dezelfde grafiek, dezelfde data; alleen barRadius verandert. Wissel van renderer: svg en canvas ronden de hoeken identiek af.

tsx
<ComparableVerticalBarChart {...props} barRadius={0} />
vue
<ComparableVerticalBarChart :options="{ ...props, barRadius: 0 }" />
svelte
<div use:comparableVerticalBarChart={{ ...props, barRadius: 0 }}></div>
ts
applyComparableVerticalBarChartProps(this.c.nativeElement, { ...props, barRadius: 0 });
html
<michi-vz-comparable-vertical-bar-chart bar-radius="0"></michi-vz-comparable-vertical-bar-chart>
  • De getekende radius is nooit groter dan de helft van de breedte of hoogte van de balk, in svg en canvas gelijk.
  • Zonder barRadius blijft het vertrouwde uiterlijk van 5px behouden. Een negatieve waarde geeft vierkante hoeken.
  • De experimentele webgpu-renderer tekent vierkante hoeken, wat de waarde ook is.

Gebruik ​

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

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

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

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

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

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

API ​

Props zijn getypeerd als ComparableVerticalBarChartProps 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() geven de renderer-onafhankelijke ChartContext terug.

Gedragsnotities ​

Twee volledig overlappende subbalken per kolom, VASTE volgorde ​

Elke categorie tekent valueBased (de referentie-/voorgaande waarde, erachter, geschikt voor arcering) en valueCompared (de huidige waarde, ervoor, vol), beide op dezelfde x en de volledige kolombreedte - anders dan de optionele "grouped" halve-band-layout van ComparableHorizontalBarChart is deze grafiek uitsluitend overlappend. De tekenvolgorde is vast (niet breedte-afhankelijk): valueBased wordt altijd erachter getekend, valueCompared altijd ervoor - overgenomen van de BarCompare/Bar-tekenvolgorde van de legacy sdg-trade BarchartVertical. colorsBasedMapping geeft de valueBased-subbalk per label een eigen kleur: combineer een dekkende lichte tint met valueBasedOpacity: 1 voor het scherpste voor/na-contrast in beide thema's. valueBasedOpacity / valueComparedOpacity stellen hun vulopaciteit in (historisch: 0.45 / 0.9). Een subbalk waarvan de opgeloste vulling transparent is, wordt overgeslagen (consumenten verbergen zo de helft via CSS). minBarHeight (standaard 5) zorgt voor een ondergrens op een niet-nul balk, zodat waarden dicht bij nul zichtbaar blijven.

Verschilindicator - en de context van deze grafiek weerspiegelt hem ​

deltaIndicator: { show: true } tekent een verschilpijl + geformatteerd label BOVEN de hoogste van de twee subbalken (legacy-plaatsing translate(bandwidth/3, -32)). Anders dan bij ComparableHorizontalBarChart (waar de indicator puur presentationeel is), weerspiegelt de getContext() van deze grafiek de indicator: elke series[]-rij draagt deltaDirection / deltaColor / deltaLabel wanneer actief, stats.improved / stats.worsened telt de goede/slechte bewegingen, en de a11y-tabel krijgt een vijfde kolom "Change". positiveIsGood / positiveIsUp kiezen de kleur-/richtingtoewijzing (zie de JSDoc van DeltaIndicatorConfig voor de volledige beslistabel); formatter(diff, datum) neemt de volledige controle over de labeltekst. Weggelaten, of { show: false }, is een aantoonbare no-op - geen geometrie, geen .mv-delta-DOM, geen extra contextvelden.

patternsMapping - arcering / afbeeldingsvullingen ​

patternsMapping: Record<label, imageSrc> vult de value-based subbalk met een herhaalde afbeelding in plaats van een vlakke kleur. createHatchPattern({ color, angle?, spacing?, strokeWidth? }) (geëxporteerd uit @michi-vz/core en @michi-vz/react) geeft voor het gangbare geval een diagonale arcering terug als SVG data-URI. De SVG-renderer verwijst ernaar via een echte <defs><pattern>; de canvas-renderer herhaalt deze via ctx.createPattern en rendert opnieuw zodra de afbeelding geladen is.

Waarde-as (y) ​

yAxisDomain: [min, max] legt het bereik van de waarde-as vast. symmetricYDomain dwingt [-M, M] af (M = de grootste absolute waarde) zodat nul gecentreerd staat. showZeroLineForYAxis tekent een doorgetrokken lijn op y=0 (divergerende grafieken); showGrid schakelt horizontale rasterlijnen in/uit (standaard uit). yAxisFormat formatteert de tick-labels; ticks stelt het geschatte aantal in.

Categorie-as (x) ​

Kolomlabels passen horizontaal wanneer er ruimte is; anders kantelen ze -45° (of dunnen ze uit tot een leesbare subset met xAxisMode: "horizontal"), dezelfde chooseAxisMode-layout die VerticalStackBarChart gebruikt. xAxisLabelPadding verhoogt de drempel voordat een label kantelt; xAxisFormat formatteert elk label; hideTickLabels verbergt ze volledig; maxBarWidth begrenst de dikte van elke kolom (en centreert het plot) zodat een handvol categorieën niet uitgroeit tot enorme blokken.

Tooltip ​

tooltipFormatter(datum, dataSet, type) ontvangt de kolom waarover gehoverd wordt, alle kolommen, en het type van de gehoverde subbalk ("based" | "compared"). Het geeft een HTML-string terug; de React-wrapper accepteert daarnaast ook een React-node (omgezet naar statische HTML). De ingebouwde tooltip is edge-aware (klapt om nabij de rechter-/onderrand).

Laden / geen data + interactie ​

isLoading en isNodata sturen de overlay aan (React: isLoadingComponent / isNodataComponent). Hoveren markeert een kolom (andere dimmen) en mouseleave maakt dit ongedaan; de balken zijn afgerond (standaard radius 5, in te stellen met barRadius) met een rand van 1px.

Kleurautoriteit van de consument: de context bevat legendData ({ label, color, dataLabelSafe }) zodat een CSS-injectie-kleursysteem regels per label kan koppelen; onChartDataProcessed wordt alleen uitgezonden wanneer de context verandert (het opnieuw uitzenden van een ongewijzigde context bij elke render kan een consument die op elke aanroep dispatcht in een lus laten belanden).