Chart.js построен как модульная библиотека визуализации данных, ориентированная на работу через HTML5 Canvas. В основе лежит принцип разделения ответственности между слоями: подготовка данных, математическое преобразование, система отрисовки и управление состоянием графика. Такая структура позволяет изолировать логику вычислений от рендеринга и обеспечивает предсказуемое поведение при изменении данных или конфигурации.
Ключевая особенность архитектуры — наличие централизованного объекта графика, который управляет всеми компонентами: наборами данных, осями, элементами графика и плагинами. Этот объект выступает точкой координации, через которую проходят все изменения состояния.
Каждый график в Chart.js создаётся как экземпляр класса
Chart. Он содержит:
Внутри экземпляра поддерживается жизненный цикл, включающий этапы подготовки данных, расчёта масштаба, построения элементов и отрисовки.
Важный архитектурный принцип — иммутабельность входной конфигурации. Библиотека не изменяет исходный объект конфигурации напрямую, а строит внутреннее состояние на его основе.
Данные в Chart.js структурированы вокруг двух основных сущностей:
Каждый dataset содержит:
Данные не используются напрямую в отрисовке. Они проходят через слой адаптации, который преобразует их в внутренние элементы графика (points, bars, arcs и т.д.).
Отрисовка графика основана на наборе примитивов:
Каждый элемент отвечает за:
Элементы не знают о данных высокого уровня. Они работают только с уже вычисленными координатами и стилями. Это снижает связанность и упрощает расширение библиотеки.
Архитектура Chart.js разделяет логику разных типов графиков через контроллеры (Controllers).
Примеры:
Контроллер выполняет роль посредника между данными и элементами. Он:
Контроллеры являются ключевым механизмом расширяемости. Добавление нового типа графика сводится к созданию нового контроллера и набора элементов.
Оси координат реализованы через систему масштабов:
Каждый scale отвечает за:
Масштабы работают независимо от типов графиков. Один и тот же scale может использоваться в разных визуализациях, что делает архитектуру гибкой.
Chart.js использует внутренний механизм компоновки для расчёта размеров элементов:
Каждый компонент сообщает свои требования к пространству. Далее выполняется распределение доступной области с учётом приоритетов и ограничений.
Геометрическая модель строится вокруг canvas-координат, где все элементы переводятся в абсолютные пиксели перед отрисовкой.
Основной механизм вывода — HTML5 Canvas API. Chart.js не использует DOM для построения графиков, что обеспечивает:
Рендеринг проходит в несколько стадий:
Элементы отрисовываются последовательно, часто с разделением по z-index логике: фоновые слои, данные, интерактивные элементы.
Анимации основаны на интерполяции состояний между предыдущим и новым значением.
Каждый элемент хранит:
Анимационный движок управляет временем и функцией easing. При обновлении данных система не пересоздаёт график, а плавно изменяет параметры существующих элементов.
Это позволяет:
Chart.js построен как расширяемая система через плагины.
Плагины подключаются к жизненному циклу графика через хуки:
Плагин может:
Такой механизм позволяет расширять функциональность без изменения ядра библиотеки.
Интерактивность реализована через систему событий Canvas:
Chart.js преобразует координаты курсора в контекст данных графика. Далее происходит hit-testing — определение, над каким элементом находится указатель.
Результатом является набор активных элементов, которые могут подсвечиваться или использоваться для всплывающих подсказок.
Обновление графика происходит через метод update(),
который инициирует полный цикл перерасчёта:
Система оптимизирует обновления, избегая полного пересоздания графика, если изменения локальны (например, обновление данных одного dataset).
Внутренняя структура Chart.js можно представить как иерархию:
Chart (корневой объект)
Эта иерархия обеспечивает строгую модульность и разделение ответственности между слоями.
Архитектура Chart.js основана на нескольких устойчивых принципах:
Разделение данных и представления Данные не зависят от рендера и наоборот.
Композиция вместо наследования Поведение формируется через комбинацию контроллеров, элементов и плагинов.
Изоляция типов графиков Каждый тип реализуется независимо, без влияния на другие.
Минимизация прямых зависимостей Компоненты взаимодействуют через абстракции и контекст графика.
Детерминированный рендеринг При одинаковом входном состоянии результат отрисовки воспроизводим.
Типичный цикл построения графика включает последовательность:
Каждый этап строго отделён, что упрощает диагностику и расширение функциональности.
Для обеспечения производительности применяются:
Эти механизмы позволяют Chart.js эффективно работать даже с большими наборами данных и высокой частотой обновлений.