Pictogram Crowd in JavaScript

Using ApexCharts with JavaScript

This Pictogram Crowd example uses ApexCharts.js directly in JavaScript, with no wrapper component.

Install it with npm install apexcharts, then mount the chart with new ApexCharts(element, options).render().

JavaScript installation guide
// Shared by the vanilla-js, React and Vue builds. Layout and mark are two
// INDEPENDENT choices, so the state is two fields and the update payload sets
// each from its own field; nothing here knows which combination is selected.
var pictoState = { layout: 'heart', mark: 'person' }

// The updateOptions payload for the current pair. An empty mark means "no
// glyph", which is just the ordinary dot the chart drew before pictograms
// existed - `shape` carries that, so `pictogram.mark` is left alone.
function pictoOptions() {
  return {
    plotOptions: {
      unit: {
        positions: pictoState.layout,
        shape: pictoState.mark ? 'pictogram' : 'circle',
        pictogram: { mark: pictoState.mark || undefined },
      },
    },
  }
}

function pictoPaint() {
  document.querySelectorAll('#layout-nav button').forEach(function (b) {
    b.classList.toggle(
      'on',
      b.getAttribute('data-layout') === pictoState.layout,
    )
  })
  document.querySelectorAll('#mark-nav button').forEach(function (b) {
    b.classList.toggle('on', b.getAttribute('data-mark') === pictoState.mark)
  })
}

// Wire both button rows to a live chart instance. Each format hands over its
// own instance; the behaviour is identical.
function pictoWire(chart) {
  document.querySelectorAll('#layout-nav button').forEach(function (b) {
    b.addEventListener('click', function () {
      pictoState.layout = b.getAttribute('data-layout')
      chart.updateOptions(pictoOptions(), false, true)
      pictoPaint()
    })
  })
  document.querySelectorAll('#mark-nav button').forEach(function (b) {
    b.addEventListener('click', function () {
      pictoState.mark = b.getAttribute('data-mark')
      chart.updateOptions(pictoOptions(), false, true)
      pictoPaint()
    })
  })
  pictoPaint()
}

var options = {
  series: [820, 410, 170],
  chart: {
    type: 'unit',
    id: 'pictoCrowd',
    height: 460,
    animations: {
      enabled: true,
      speed: 900,
    },
  },
  labels: ['Weekday shifts', 'Weekend shifts', 'On call'],
  colors: ['#1D4E89', '#00A6A0', '#FF8A3D'],
  plotOptions: {
    unit: {
      layout: 'custom',
      positions: 'heart',
      // A glyph, not a dot. `mark` names one of the pictograms; the chart fits
      // it to the box the dot would have occupied, so switching between them
      // never re-flows the layout.
      shape: 'pictogram',
      pictogram: {
        mark: 'person',
      },
      // The same specific volunteer keeps their glyph and colour when the layout
      // changes, so a shape swap reads as the crowd walking rather than as a
      // slideshow.
      transition: 'flow',
      unitValue: 10,
      clusterLabels: {
        show: false,
      },
    },
  },
  legend: {
    position: 'bottom',
  },
}

var chart = new ApexCharts(document.querySelector('#chart'), options)
chart.render()

// Layout and mark buttons. (pictoState, pictoOptions and pictoWire live in the
// shared head script.)
pictoWire(chart)