Веб

Как выровнять оси нескольких графиков в Highcharts

Синхронизация осей и выравнивание нескольких графиков в Highcharts: решение проблемы непредсказуемого рендера заголовков. Пошаговая инструкция с кодом, marginLeft, plotLeft и обработкой особых случаев как opposite yAxis.

Как выровнять несколько графиков относительно друг друга в библиотеке Highcharts, чтобы заголовки осей рендерились предсказуемо?

У меня есть 3 графика, но рендеринг заголовков осей происходит непредсказуемо. Пытался установить фиксированную ширину заголовков — не сработало. Рассматривал вариант: найти максимальную длину значения и для более коротких добавлять отступы графика. Есть ли готовое решение для синхронизации графиков по осям в Highcharts?

Готового «одной кнопкой» решения для автоматического выравнивания нескольких отдельных экземпляров highcharts нет; стандартный и надёжный способ — программно синхронизировать отступы/поля (например, установить одинаковый chart.marginLeft/marginRight/marginBottom или привести plotLeft к одному значению). Это даёт предсказуемый рендер заголовков осей и легко работает с динамическими данными — достаточно выполнить выравнивание после рендера и при изменениях. Для сложных случаев полезно комбинировать этот подход с опциями вроде chart.alignTicks и yAxis.offset.


Содержание


Почему заголовки осей рендерятся непредсказуемо в highcharts

Причин несколько — и нужно понимать их, чтобы выбирать правильный фикс:

  • Highcharts автоматически рассчитывает отступы вокруг plot area, учитывая ширину меток осей, легенду, заголовки и типы серий. Это значит, что два соседних графика с разной длиной y‑меток получат разные plotLeft / marginLeft.
  • Некоторые типы серий (например, column или bubble) добавляют дополнительные внутренние отступы или padding к осям — из-за этого смещения разные графики выглядят «несинхронизированными» по осям. (см. обсуждение в форуме: колонны и bubble увеличивают padding) https://www.highcharts.com/forum/viewtopic.php?t=34279.
  • Настройка выравнивания тиков внутри одного чарта (chart.alignTicks) полезна для нескольких осей в одном графике, но не выравнивает отдельные инстансы графиков на странице https://api.highcharts.com/highcharts/chart.alignTicks.
  • Легенда, подписи, плавающие элементы и разная конфигурация axis.title/axis.labels меняют итоговые метрики (plotLeft, marginBottom и т.д.), поэтому простая «фиксированная ширина заголовков» часто не срабатывает.

Готовое решение: синхронизация осей и выравнивание графиков в highcharts

Общая идея простая и проверенная на практике: измерить реальные позиционные значения после рендера (plotLeft, правая пустая область, marginBottom) и установить одинаковые отступы для всех графиков. То есть:

  • после initial render собрать у всех chart.plotLeft, chart.chartWidth, chart.plotWidth, chart.marginBottom;
  • вычислить максимальные значения (maxPlotLeft, maxRightGap, maxMarginBottom);
  • применить их ко всем графикам через chart.update({ chart: { marginLeft: …, marginRight: …, marginBottom: … } }, false) и затем единоразово вызвать chart.redraw() для каждого.

Опция chart.marginLeft прямо позволяет переопределить авто‑расчёт отступа и «зафиксировать» старт plot area: https://api.highcharts.com/highcharts/chart.marginLeft. Форум Highcharts рекомендовал подход с установкой одинакового marginLeft/ tickInterval / min/max чтобы добиться предсказуемого выравнивания https://www.highcharts.com/forum/viewtopic.php?t=19921.


Пошаговая инструкция: выравнивание графиков (пример кода)

Ниже минимальный и практичный рецепт. Идея — собирать графики в массив и вызывать функцию выравнивания после загрузки каждого графика и при изменениях.

Пример (упрощённый):

javascript
// Массив, в который мы складываем объекты Highcharts.Chart
var charts = [];

// Функция выравнивания
function alignCharts(charts) {
 var maxPlotLeft = 0;
 var maxRightGap = 0;
 var maxMarginBottom = 0;

 charts.forEach(function(ch) {
 if (!ch) return;
 if (ch.plotLeft > maxPlotLeft) maxPlotLeft = ch.plotLeft;
 var rightGap = ch.chartWidth - ch.plotLeft - ch.plotWidth; // правая пустая зона
 if (rightGap > maxRightGap) maxRightGap = rightGap;
 if (ch.marginBottom > maxMarginBottom) maxMarginBottom = ch.marginBottom;
 });

 // Обновляем без перерисовки, чтобы сделать один общий redraw в конце
 charts.forEach(function(ch) {
 if (!ch) return;
 ch.update({
 chart: {
 marginLeft: Math.round(maxPlotLeft),
 marginRight: Math.round(maxRightGap),
 marginBottom: Math.round(maxMarginBottom)
 }
 }, false); // redraw = false
 });

 // Теперь единоразово перерисуем
 charts.forEach(function(ch) { if (ch) ch.redraw(); });
}

// Пример создания чарта — вызываем alignCharts в событии load
function createChart(container, options) {
 options.chart = options.chart || {};
 options.chart.events = options.chart.events || {};
 var userLoad = options.chart.events.load;
 options.chart.events.load = function() {
 if (userLoad) userLoad.apply(this, arguments);
 // Добавляем chart в массив (если ещё не добавлен)
 if (charts.indexOf(this) === -1) charts.push(this);
 // Выравниваем после рендера
 alignCharts(charts);
 };
 var chart = Highcharts.chart(container, options);
 return chart;
}

Ключевые моменты:

  • Обновляем все графики с redraw = false, затем делаем redraw в цикле — это экономнее и предотвращает лишние reflow.
  • Вызывать alignCharts и при resize (с debounce), и после динамических обновлений данных.
  • В сложных случаях вычисления можно заменить на измерение конкретных label bounding box (getBBox) — но plotLeft/plotWidth/chartWidth обычно достаточно и надёжнее.

Особые случаи: opposite yAxis, легенда, разные типы серий

  • Opposite yAxis (ось справа): используйте marginRight по аналогии с marginLeft или корректируйте yAxis.offset у правой оси. Форум предлагает именно либо фиксировать marginRight для всех чартах, либо подгонять offset у opposite‑оси https://www.highcharts.com/forum/viewtopic.php?t=40249.
  • Легенда влияет на marginBottom: если легенда внизу у одного графика, а у другого — сбоку, x‑оси не совпадут по вертикали. Совет — либо делать легенду плавающей (floating), либо синхронизировать marginBottom, как предлагают в обсуждении https://www.highcharts.com/forum/viewtopic.php?t=38991.
  • Column / bubble: эти типы добавляют дополнительные padding — учтите это при вычислениях (см. комментарии в форуме) https://www.highcharts.com/forum/viewtopic.php?t=34279.
  • Синхронизация тиков: если нужно не только визуально выровнять plot area, но и сделать одинаковые шаги меток — выставьте одинаковые tickInterval/tickPositions или min/max у осей. Это предотвратит расхождение в количестве тиков.

Практические советы и производительность

  • Не делайте chart.update с redraw = true внутри цикла — это дорого. Обновляйте с redraw = false и потом делайте единоразовый redraw.
  • Подвешивайте выравнивание на события chart.events.load и chart.events.redraw (если у вас динамическое обновление данных).
  • Добавьте debounce для window.resize, чтобы не вызывать выравнивание десятки раз при перетаскивании окна.
  • Если у вас фиксированный набор меток, проще заранее задать marginLeft в опциях (chart.marginLeft) — это решит проблему без JS‑подгонок https://api.highcharts.com/highcharts/chart.marginLeft.
  • В некоторых случаях полезно отключить alignTicks (chart.alignTicks = false), чтобы Highcharts не подгонял количество тиков автоматически внутри чарта, если вы хотите полный контроль над отступами https://api.highcharts.com/highcharts/chart.alignTicks.

Источники


Заключение

Коротко: готового «всё‑само» механизма для выравнивания нескольких отдельных экземпляров highcharts нет, зато есть надёжный рабочий рецепт — вычислить реальные plotLeft/marginBottom/правая зона и программно установить одинаковые chart.marginLeft/marginRight/marginBottom для всех графиков, выполняя обновления без немедленного redraw и затем единоразово перерисовав. Такой подход решает проблему непредсказуемого рендера заголовков осей и является де-факто стандартом для синхронизации осей и выравнивания графиков в Highcharts.

Авторы
Проверено модерацией
Модерация
Как выровнять оси нескольких графиков в Highcharts