<template>
  <div>
    <div id="chart">
      <apexchart
        type="scatter"
        height="460"
        :options="chartOptions"
        :series="series"
      ></apexchart>
    </div>
  </div>
</template>

<script>
import VueApexCharts from 'vue-apexcharts'

// ── Reproducible synthetic data (seeded PRNG, no network) ──────────────
function mulberry32(a) {
  return function () {
    a |= 0
    a = (a + 0x6d2b79f5) | 0
    var t = Math.imul(a ^ (a >>> 15), 1 | a)
    t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
    return ((t ^ (t >>> 14)) >>> 0) / 4294967296
  }
}
var rng = mulberry32(20240607)

function gauss(mean, sd) {
  var u = 0,
    v = 0
  while (u === 0) u = rng()
  while (v === 0) v = rng()
  return mean + sd * Math.sqrt(-2 * Math.log(u)) * Math.cos(2 * Math.PI * v)
}

// Individual API response-time readings per data-centre region. Compact
// strip-plot form: { x: 'Region', y: [reading, reading, ...] }. Every reading
// becomes its own hoverable marker, scattered horizontally within its band.
var REGIONS = [
  { name: 'Frankfurt', mean: 120, sd: 22 },
  { name: 'Mumbai', mean: 182, sd: 30 },
  { name: 'Oregon', mean: 96, sd: 18 },
  { name: 'Singapore', mean: 158, sd: 26 },
  { name: 'Virginia', mean: 82, sd: 16 },
]

function readings(region) {
  var out = []
  for (var i = 0; i < 150; i++) {
    out.push(Math.max(20, Math.round(gauss(region.mean, region.sd))))
  }
  return out
}

export default {
  components: {
    apexchart: VueApexCharts,
  },
  data: function () {
    return {
      series: [
        {
          name: 'Frankfurt',
          data: [{ x: 'Frankfurt', y: readings(REGIONS[0]) }],
        },
        {
          name: 'Mumbai',
          data: [{ x: 'Mumbai', y: readings(REGIONS[1]) }],
        },
        {
          name: 'Oregon',
          data: [{ x: 'Oregon', y: readings(REGIONS[2]) }],
        },
        {
          name: 'Singapore',
          data: [{ x: 'Singapore', y: readings(REGIONS[3]) }],
        },
        {
          name: 'Virginia',
          data: [{ x: 'Virginia', y: readings(REGIONS[4]) }],
        },
      ],
      chartOptions: {
        chart: {
          type: 'scatter',
          height: 460,
        },
        title: {
          text: 'API response time by region',
          align: 'center',
        },
        plotOptions: {
          scatter: {
            // Spread the readings in each band horizontally (axis units; 1 = one
            // band step). Set `distributed: true` to colour each band differently.
            jitter: {
              enabled: true,
              x: 0.35,
            },
          },
        },
        markers: {
          size: 3,
          strokeWidth: 0,
        },
        fill: {
          opacity: 0.6,
        },
        legend: { show: false },
        xaxis: {
          // strip plots read better without the vertical crosshair line
          crosshairs: { show: false },
        },
        yaxis: {
          title: { text: 'Response time (ms)' },
          labels: {
            formatter: function (v) {
              return Math.round(v)
            },
          },
        },
        tooltip: {
          // anchor the tooltip to the hovered band so it stays put instead of
          // chasing each individual marker
          shared: true,
          intersect: false,
          y: {
            formatter: function (v) {
              return Math.round(v) + ' ms'
            },
          },
        },
      },
    }
  },
}
</script>

<style>
#chart {
  max-width: 760px;
  margin: 20px auto;
}
</style>
Scatter with Jitter - Vue Scatter Charts | ApexCharts.js | ApexCharts.js