Пузырьковая диаграмма (bubble)

Пузырьковая диаграмма в Chart.js расширяет точечную диаграмму за счёт третьего измерения — радиуса окружности. Если в scatter-графике каждая точка задаётся координатами x и y, то в bubble-доске добавляется параметр r, определяющий размер пузырька.

Базовая структура данных выглядит следующим образом:

const data = {
  datasets: [
    {
      label: 'Набор данных',
      data: [
        { x: 10, y: 20, r: 8 },
        { x: 15, y: 10, r: 12 },
        { x: 25, y: 30, r: 6 }
      ]
    }
  ]
};

Каждый объект в массиве data содержит:

  • x — значение по горизонтальной оси
  • y — значение по вертикальной оси
  • r — радиус пузырька, влияющий на визуальный размер точки

Радиус не является прямым значением масштаба в пикселях, Chart.js дополнительно нормализует его в зависимости от области отрисовки.


Инициализация графика

Для создания пузырьковой диаграммы используется тип bubble:

const config = {
  type: 'bubble',
  data: data,
  options: {
    responsive: true,
    scales: {
      x: {
        title: {
          display: true,
          text: 'Ось X'
        }
      },
      y: {
        title: {
          display: true,
          text: 'Ось Y'
        }
      }
    }
  }
};

new Chart(
  document.getElementById('chart'),
  config
);

Ключевой момент — обязательное использование числовых шкал (linear, logarithmic), поскольку категориальные оси не поддерживают корректную интерпретацию координат пузырьков.


Принцип масштабирования радиуса

Значение r не отображается напрямую как пиксельный радиус. Chart.js использует внутреннюю формулу нормализации:

  • минимальные и максимальные значения r в наборе данных вычисляются автоматически
  • далее происходит масштабирование относительно доступного пространства

Это позволяет избежать ситуации, когда один большой пузырёк перекрывает весь график.

Контроль поведения осуществляется через параметр pointRadius и настройки элементов:

options: {
  elements: {
    point: {
      borderWidth: 1
    }
  }
}

Настройка внешнего вида пузырьков

Цвет и стилизация задаются на уровне датасета:

datasets: [
  {
    label: 'Продажи',
    data: [
      { x: 5, y: 10, r: 10 },
      { x: 10, y: 15, r: 20 }
    ],
    backgroundColor: 'rgba(54, 162, 235, 0.5)',
    borderColor: 'rgba(54, 162, 235, 1)',
    borderWidth: 1
  }
]

Каждый пузырёк наследует стиль набора данных, но можно задавать индивидуальные цвета:

data: [
  { x: 10, y: 20, r: 8, backgroundColor: 'red' },
  { x: 15, y: 10, r: 12, backgroundColor: 'green' }
]

Несколько наборов данных

Bubble-диаграмма поддерживает несколько серий, что позволяет сравнивать группы объектов:

datasets: [
  {
    label: 'Группа A',
    data: [
      { x: 10, y: 20, r: 8 },
      { x: 15, y: 25, r: 10 }
    ]
  },
  {
    label: 'Группа B',
    data: [
      { x: 12, y: 18, r: 12 },
      { x: 20, y: 30, r: 6 }
    ]
  }
]

Каждая серия отображается отдельным цветом, а легенда автоматически формируется на основе label.


Оси координат и их настройка

Пузырьковая диаграмма чувствительна к настройкам осей. Основные параметры:

scales: {
  x: {
    type: 'linear',
    min: 0,
    max: 50
  },
  y: {
    type: 'linear',
    min: 0,
    max: 50
  }
}

Фиксация диапазонов позволяет стабилизировать визуализацию при динамических данных.

Дополнительно можно управлять сеткой:

grid: {
  display: true,
  color: 'rgba(200, 200, 200, 0.2)'
}

Обработка всплывающих подсказок

Tooltip в bubble-диаграммах часто требует кастомизации, так как кроме x и y важно отображать r:

options: {
  plugins: {
    tooltip: {
      callbacks: {
        label: function(context) {
          const point = context.raw;
          return `x: ${point.x}, y: ${point.y}, r: ${point.r}`;
        }
      }
    }
  }
}

Это позволяет явно интерпретировать размер пузырька как дополнительную метрику.


Сортировка и визуальное перекрытие

При работе с большим количеством точек возникает проблема перекрытия пузырьков. Chart.js рисует элементы в порядке массива данных.

Для управления визуальной иерархией используется сортировка:

data.sort((a, b) => a.r - b.r);

Меньшие пузырьки можно рисовать поверх крупных или наоборот — в зависимости от цели визуализации.


Анимации

Bubble-графики поддерживают стандартную систему анимаций Chart.js:

options: {
  animation: {
    duration: 1000,
    easing: 'easeOutQuart'
  }
}

При обновлении данных каждый пузырёк плавно изменяет позицию и радиус.


Динамическое обновление данных

Изменение данных выполняется через update():

chart.data.datasets[0].data.push({ x: 30, y: 40, r: 15 });
chart.update();

Удаление элементов:

chart.data.datasets[0].data.pop();
chart.update();

Полная замена набора данных:

chart.data.datasets[0].data = newData;
chart.update();

Производительность при больших объёмах

При тысячах точек bubble-диаграмма может замедляться из-за:

  • расчёта радиусов
  • перерисовки canvas
  • обработки событий hover

Оптимизации:

  • уменьшение количества точек через агрегацию
  • отключение лишних анимаций
  • упрощение tooltip
  • использование decimation (в новых версиях Chart.js)

Взаимодействие с событиями

Chart.js позволяет отслеживать клики по пузырькам:

options: {
  onClick: (event, elements) => {
    if (elements.length > 0) {
      const el = elements[0];
      const data = el.element.$context.raw;
      console.log(data);
    }
  }
}

Каждый элемент содержит доступ к исходному объекту данных, включая r.


Масштабирование радиусов под бизнес-данные

При использовании реальных метрик (например, продажи, население, стоимость) часто требуется нормализация:

const max = Math.max(...values);
const normalized = values.map(v => ({
  x: v.x,
  y: v.y,
  r: (v.value / max) * 20
}));

Это предотвращает доминирование одного значения над остальными и делает распределение визуально читаемым.


Комбинирование с другими типами графиков

Bubble-диаграммы иногда комбинируются с линейными или scatter-графиками в одном canvas через мульти-датасеты:

datasets: [
  {
    type: 'bubble',
    data: [...]
  },
  {
    type: 'line',
    data: [...]
  }
]

Это позволяет накладывать тренды поверх распределения точек и анализировать зависимости более глубоко.