Шкалы в Chart.js представляют собой отдельный слой системы визуализации, отвечающий за преобразование входных данных в координаты на графике. Любая диаграмма, независимо от типа, опирается на минимум одну шкалу, которая определяет, как значения интерпретируются и отображаются на осях.
Внутренняя архитектура Chart.js построена вокруг модульной системы, где шкалы являются независимыми компонентами. Каждая шкала:
Связь между датасетами и шкалами задаётся через свойства
xAxisID и yAxisID. Это позволяет одному
графику использовать несколько независимых осей, включая
пользовательские реализации.
Шкала описывается как класс, наследующий базовый Scale,
который предоставляет общий контракт поведения: обработку данных,
построение диапазона и отрисовку.
Chart.js предоставляет несколько встроенных типов шкал, каждый из которых решает определённый класс задач.
Категориальная шкала (category scale) Используется для дискретных значений. Преобразует строки в равномерно распределённые позиции.
Особенности:
Линейная шкала (linear scale) Работает с числовыми значениями и обеспечивает равномерное распределение.
Характеристики:
Логарифмическая шкала (logarithmic scale) Применяется для экспоненциальных данных.
Особенности:
Временная шкала (time scale) Использует адаптеры даты и времени.
Особенности:
Радиальная шкала (radial scale) Используется в radar и polarArea диаграммах.
Особенности:
Механизм расширения Chart.js построен на регистрации классов шкал в глобальном реестре. Каждая новая шкала должна быть зарегистрирована перед использованием.
Регистрация выполняется через систему регистрации компонентов:
import { Chart } from 'chart.js';
class CustomScale extends Chart.Scale {
constructor(cfg) {
super(cfg);
}
parse(raw, index) {
return Number(raw);
}
determineDataLimits() {
const { min, max } = this.getMinMax(true);
this.min = min;
this.max = max;
}
buildTicks() {
const ticks = [];
for (let i = this.min; i <= this.max; i += 10) {
ticks.push({ value: i });
}
return ticks;
}
getPixelForValue(value) {
const range = this.max - this.min;
const percent = (value - this.min) / range;
return this.left + percent * this.width;
}
getValueForPixel(pixel) {
const percent = (pixel - this.left) / this.width;
return this.min + percent * (this.max - this.min);
}
}
Chart.register(CustomScale);
После регистрации шкала становится доступной через конфигурацию
type.
Каждая шкала проходит несколько этапов обработки данных:
1. Инициализация Создание экземпляра и привязка к конфигурации графика.
2. Парсинг данных Метод parse()
преобразует входные значения датасетов в внутренний формат.
3. Определение диапазона Метод
determineDataLimits() вычисляет min/max.
4. Построение тиков Метод buildTicks()
формирует набор меток.
5. Расчёт координат Методы
getPixelForValue() и getValueForPixel()
выполняют двунаправленное преобразование.
6. Отрисовка Включает построение оси, сетки и подписей.
Каждый датасет может явно указывать, к какой шкале он привязан:
datasets: [
{
label: 'Продажи',
data: [10, 20, 30],
yAxisID: 'y'
}
]
Если yAxisID не задан, используется шкала по
умолчанию.
Шкалы могут быть наложены друг на друга, создавая многомерные визуализации, где разные наборы данных имеют разные диапазоны и единицы измерения.
Одним из ключевых механизмов расширения является контроль над построением меток оси.
buildTicks() {
const step = this.options.stepSize || 5;
const ticks = [];
for (let v = this.min; v <= this.max; v += step) {
ticks.push({
value: v,
label: `~${v}`
});
}
return ticks;
}
Дополнительно может использоваться метод formatTicks,
позволяющий изменить отображаемый текст без изменения значений.
Chart.js разделяет внутренние значения и отображаемые подписи. Это позволяет сохранять точность данных при гибком UI-форматировании.
Ключевые методы:
valueToLabelConversionformatValueafterTickToLabelConversionПример форматирования:
options: {
scales: {
y: {
ticks: {
callback: (value) => `${value} ₽`
}
}
}
}
В кастомной шкале аналогичное поведение реализуется через переопределение метода:
formatValue(value) {
return `${value.toFixed(2)} units`;
}
Любая шкала обязана реализовать преобразование между значением и пикселем.
Линейное преобразование выражается через нормализацию:
percent = (value - min) / (max - min)
pixel = start + percent * length
Обратное преобразование:
value = min + (pixel - start) / length * (max - min)
Эти формулы являются основой большинства кастомных реализаций шкал.
Chart.js поддерживает конфигурации, где несколько шкал работают параллельно:
scales: {
y: {
type: 'linear',
position: 'left'
},
y2: {
type: 'linear',
position: 'right'
}
}
Это позволяет разделять данные по масштабу без искажения визуального восприятия.
Кастомные шкалы могут комбинировать разные стратегии отображения, например, логарифмическое преобразование с линейной отрисовкой.
Шкалы тесно связаны с системой плагинов Chart.js. Плагины могут:
Пример перехвата тиков:
const plugin = {
beforeBuildTicks(scale) {
if (scale.id === 'y') {
scale.options.ticks.stepSize = 2;
}
}
};
Кастомные шкалы применяются в случаях, когда стандартные типы не покрывают задачу:
Пример нелинейной шкалы с сегментацией:
getPixelForValue(value) {
if (value < 50) {
return this.left + (value / 50) * (this.width * 0.3);
}
return this.left + this.width * 0.3 + ((value - 50) / 50) * (this.width * 0.7);
}
Автоматическое определение границ реализуется через анализ датасетов.
Типовой алгоритм:
determineDataLimits() {
const values = this.getMatchingVisibleMetas()
.flatMap(meta => meta.data);
this.min = Math.min(...values);
this.max = Math.max(...values);
const padding = (this.max - this.min) * 0.1;
this.min -= padding;
this.max += padding;
}
Шкалы пересчитываются при изменении датасетов. При этом учитываются режимы обновления:
update(): полное пересчитывание;resize(): изменение геометрии без пересчёта
данных;reset(): сброс анимационного состояния.В сложных сценариях кастомные шкалы могут кэшировать промежуточные вычисления для повышения производительности.
Базовый класс Scale предоставляет набор методов, которые
могут быть переопределены:
init()parse()buildTicks()getPixelForValue()getValueForPixel()draw()Корректная реализация кастомной шкалы требует согласованности между всеми методами, так как нарушение соответствия приводит к рассинхронизации данных и визуализации.
Шкалы взаимодействуют с несколькими системами:
Любое изменение шкалы отражается на всех слоях визуализации, поскольку координатная система является центральным элементом рендеринга.