Aller au contenu

Ajouter et retirer des données ​

Affectez les données d'une série et tout ce qui la dessine suit : les barres, la courbe, l'infobulle, et les axes quand un domaine automatique les ajuste.

ts
import {
  D3PAutoDomain, d3pAutoInteractionArea, D3PAxis, D3PAxisDomain, D3PBarRenderer, D3PChart, D3PCursorIndicator,
  d3pDomTooltip, D3PLineRenderer, D3PSeries, D3PTooltip, getGlobalBus,
} from 'd3pict'

const bus = getGlobalBus()

const container = document.getElementById('app')! // any element with a size: the chart fills it

const sales = new D3PSeries({ bus, name: 'sales', data: [[0, 34], [1, 52], [2, 41], [3, 67], [4, 58], [5, 73], [6, 49], [7, 62]] })

// the domains follow the data: they are fitted again each time it changes
const dx = new D3PAxisDomain({ bus, domain: [0, 7] })
const dy = new D3PAxisDomain({ bus, domain: [0, 80] })
const auto = new D3PAutoDomain({ bus })
auto.linkSeries(sales, [dx, dy])

const x = new D3PAxis({ bus, name: 'month', domain: dx, props: { tickFormat: (v) => `${Math.round(v)}`, grid: { enabled: true } } })
const y = new D3PAxis({ bus, name: 'sales', domain: dy, position: 'left', props: { grid: { enabled: true } } })
const bars = new D3PBarRenderer({ bus, series: sales, marker: false, props: { style: { color: '#9ccc96', barWidth: 18 } } })
const line = new D3PLineRenderer({ bus, series: sales, props: { style: { color: '#4e79a7', curve: 'monotone', strokeWidth: 2.5 } } })

const cursor = new D3PCursorIndicator({ bus })
const tooltip = new D3PTooltip({ bus })
const objects = [x, y, bars, line, cursor, tooltip]

// changing the data is assigning the series: what draws it moves from what was there to what is there now
const value = () => Math.round(20 + Math.random() * 60)
const actions: Record<string, () => void> = {
  'add a point': () => {
    const last = sales.data.at(-1)
    sales.data = [...sales.data, [last ? +last[0] + 1 : 0, value()]]
  },
  'remove the last': () => (sales.data = sales.data.slice(0, -1)),
  'remove the first': () => (sales.data = sales.data.slice(1)),
  'replace': () => (sales.data = Array.from({ length: 5 + Math.floor(Math.random() * 8) }, (_, i) => [i, value()])),
}

const bar = Object.assign(document.createElement('div'), { className: 'toolbar' })
container.before(bar)
// a button for each way to change the data
for (const [name, run] of Object.entries(actions)) bar.append(Object.assign(document.createElement('button'), { textContent: name, onclick: run }))

const chart = new D3PChart({ container, bus })
const stopArea = d3pAutoInteractionArea(chart) // before the objects: the cursor and the tooltip ask for an interaction area
objects.forEach((o) => o.addToChart(chart))
const stopTooltip = d3pDomTooltip(tooltip)

Ce qu'il montre ​

  • Changer les données, c'est les affecter. series.data = [...series.data, [x, valeur]] ajoute un point ; series.data.slice(1) retire le premier. Il n'y a pas d'autre appel : une série annonce que ses données ont changé, et les rendus et le domaine automatique qui lui sont liés réagissent.
  • Le graphique passe de ce qui était là à ce qui est là maintenant. Un point nouveau naît sur son plus proche voisin et un point retiré meurt sur lui, donc une courbe grandit ou rétrécit sans sauter ; une nouvelle barre grandit depuis la base et une barre retirée y retourne. La transition dure 200 ms, et repart de ce qui est à l'écran si les données changent pendant qu'elle a lieu. Avancé donne l'algorithme.
  • Les axes s'ajustent aux nouvelles données. Le D3PAutoDomain lié à la série règle de nouveau les domaines x et y à chaque fois. Sans lui, les domaines gardent les plages que vous avez écrites et les nouveaux points peuvent tomber dehors.
  • Le zoom et le déplacement ne sont pas animés. Un changement de domaine est dessiné aussitôt, pour que le glissement ne traîne jamais derrière le pointeur.