В 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. После изменения структуры
обязательно вызывается перерисовка графика.
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 выступает как слой визуализации, полностью отделённый от источника данных.
Каждый 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');
Удаление набора данных производится через 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;
Вместо полной замены массива часто используется точечное обновление значений внутри 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();
}
Это позволяет интегрировать график в реактивные системы, где данные могут изменяться десятки раз в секунду.
При работе с несколькими наборами данных важно учитывать визуальную перегрузку и порядок отрисовки:
chart.data.datasets.push(
{
label: 'A',
data: [1, 2, 3],
borderWidth: 2
},
{
label: 'B',
data: [3, 2, 1],
borderWidth: 2
}
);
chart.update();
Порядок влияет на наложение линий и столбцов: последний dataset отображается поверх предыдущих.
Полная перезагрузка графика может быть выполнена через обнуление массива:
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();
}
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();
}
При частых запросах используется дебаунсинг или отмена предыдущих операций, чтобы избежать неконсистентного состояния визуализации.