Динамическое добавление и удаление datasets

В Chart.js модель данных строится вокруг объекта конфигурации графика, где ключевую роль играет массив datasets. Каждый элемент массива представляет собой независимый набор данных со своими стилями, метками и значениями. Такая структура позволяет гибко управлять визуализацией в рантайме без пересоздания экземпляра графика.

В типичном сценарии график создаётся один раз, после чего изменяются только данные:

const chart = new Chart(ctx, {
  type: 'line',
  data: {
    labels: ['Янв', 'Фев', 'Мар', 'Апр'],
    datasets: [
      {
        label: 'Продажи',
        data: [10, 20, 15, 30],
        borderColor: 'blue'
      }
    ]
  }
});

Любая динамика в Chart.js сводится к манипуляциям с chart.data.datasets и последующему вызову chart.update().


Базовый механизм добавления datasets

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

chart.data.datasets.push({
  label: 'Расходы',
  data: [5, 12, 8, 20],
  borderColor: 'red'
});

chart.update();

Ключевой момент заключается в синхронизации длины массива data с количеством меток labels. Несоответствие приводит к некорректному отображению или усечению данных.

Добавление с динамической генерацией

В реальных приложениях данные часто приходят из API:

function addDatasetFromApi(response) {
  chart.data.datasets.push({
    label: response.label,
    data: response.values,
    backgroundColor: response.color
  });

  chart.update();
}

При такой архитектуре Chart.js выступает как слой визуализации, полностью отделённый от источника данных.


Управление индексами и идентификация datasets

Каждый dataset не имеет обязательного уникального идентификатора, поэтому управление осуществляется через индекс массива. Это накладывает важное ограничение: порядок элементов становится значимым.

const index = chart.data.datasets.length;

Для более сложных сценариев используется ручное добавление идентификаторов:

chart.data.datasets.push({
  id: 'revenue_2025',
  label: 'Доход',
  data: [12, 18, 25]
});

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

const dataset = chart.data.datasets.find(d => d.id === 'revenue_2025');

Удаление datasets

Удаление набора данных производится через splice, так как массив является обычным JavaScript-объектом:

chart.data.datasets.splice(1, 1);
chart.update();

Первый аргумент — индекс, второй — количество удаляемых элементов.

Удаление по условию

При наличии идентификаторов удаление становится более управляемым:

chart.data.datasets = chart.data.datasets.filter(
  d => d.id !== 'revenue_2025'
);

chart.update();

Такой подход предпочтителен в приложениях с частыми изменениями структуры графика.


Полная синхронизация данных при изменениях

При динамическом обновлении часто требуется синхронизация не только datasets, но и labels. Например, при смене периода:

chart.data.labels = ['2021', '2022', '2023'];

chart.data.datasets = [
  {
    label: 'Прибыль',
    data: [100, 120, 150]
  },
  {
    label: 'Расходы',
    data: [80, 90, 110]
  }
];

chart.update();

Важно соблюдать соответствие размеров массивов, иначе визуализация теряет корректность.


Анимации при добавлении и удалении

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

const chart = new Chart(ctx, {
  type: 'bar',
  data,
  options: {
    animation: {
      duration: 600,
      easing: 'easeOutQuart'
    }
  }
});

При массовых обновлениях полезно временно отключать анимацию:

chart.options.animation = false;
chart.update();
chart.options.animation = true;

Инкрементальное обновление datasets

Вместо полной замены массива часто используется точечное обновление значений внутри dataset:

chart.data.datasets[0].data.push(42);
chart.data.labels.push('Май');

chart.update();

Такой подход снижает нагрузку и сохраняет контекст графика.


Обновление без пересоздания графика

Ключевая концепция Chart.js заключается в том, что экземпляр графика остаётся неизменным, изменяется только его состояние.

function updateChart(newData) {
  chart.data.datasets[0].data = newData;
  chart.update();
}

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


Сложные сценарии: множественные datasets

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

chart.data.datasets.push(
  {
    label: 'A',
    data: [1, 2, 3],
    borderWidth: 2
  },
  {
    label: 'B',
    data: [3, 2, 1],
    borderWidth: 2
  }
);

chart.update();

Порядок влияет на наложение линий и столбцов: последний dataset отображается поверх предыдущих.


Очистка всех datasets

Полная перезагрузка графика может быть выполнена через обнуление массива:

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

Альтернативный вариант — сохранение структуры с последующим заполнением:

while (chart.data.datasets.length > 0) {
  chart.data.datasets.pop();
}

chart.update();

Работа с большими потоками данных

При частых обновлениях (например, телеметрия или финансовые котировки) применяется стратегия пакетного обновления:

chart.data.datasets[0].data.push(newPoint);

if (chart.data.datasets[0].data.length > 100) {
  chart.data.datasets[0].data.shift();
}

chart.update('none');

Параметр 'none' отключает анимацию, повышая производительность.


Оптимизация производительности при динамике

При интенсивных изменениях datasets критичны следующие приёмы:

  • минимизация вызовов chart.update()
  • группировка изменений перед перерисовкой
  • ограничение длины массивов данных
  • отключение анимаций в режиме реального времени

Пример пакетного обновления:

function batchUpdate(updates) {
  updates.forEach(u => {
    chart.data.datasets[u.index].data = u.data;
  });

  chart.update();
}

Изменение структуры datasets без потери состояния

Chart.js сохраняет внутренние ссылки на объекты datasets, поэтому важно избегать полной замены объекта при необходимости сохранить интерактивные состояния:

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

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


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

При использовании разных типов визуализации в одном графике (например, line + bar) структура datasets может содержать разные type:

chart.data.datasets.push({
  type: 'line',
  label: 'Тренд',
  data: [10, 15, 20]
});

chart.data.datasets.push({
  type: 'bar',
  label: 'Факт',
  data: [12, 14, 18]
});

chart.update();

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


Согласованность состояния при асинхронных обновлениях

При работе с API важно предотвращать гонки обновлений:

async function refreshChart() {
  const res = await fetch('/api/data');
  const data = await res.json();

  chart.data.datasets = data.datasets;
  chart.data.labels = data.labels;

  chart.update();
}

При частых запросах используется дебаунсинг или отмена предыдущих операций, чтобы избежать неконсистентного состояния визуализации.