Введение
Метки в столбчатых диаграммах часто сталкиваются с проблемами читаемости, когда диаграмма изменяет размер, отображается на разных разрешениях экрана или увеличивается/уменьшается с помощью элементов управления увеличением amCharts. По умолчанию метки в amCharts 4 имеют фиксированную ширину, что может привести к наложению, обрезанию или нечитаемому тексту на маленьких экранах.
Чтобы сделать метки действительно отзывчивыми, нам нужно:
- Динамически рассчитывать максимальную ширину меток на основе размера диаграммы и ширины столбца.
- Обрезать длинные метки, если это необходимо, чтобы предотвратить переполнение.
- Использовать хуки событий, чтобы убедиться, что метки корректно подстраиваются, когда:
- Диаграмма изменяет размер.
- Происходит увеличение/панорамирование.
- Данные обновляются динамически.
В этой статье мы рассмотрим, как динамически регулировать ширину меток и обрезать метки при необходимости с помощью событий amCharts 4.
Код реализации с проблемой:
am4core.ready(function () {
// Создаем экземпляр диаграммы
let chart = am4core.create("chartdiv", am4charts.XYChart);
chart.hiddenState.properties.opacity = 0; // Начальное появление
chart.logo.disabled = true;
chart.responsive.enabled = true; // Включаем отзывчивое поведение
// Данные диаграммы
chart.data = [
{ company: "Apple Inc.", visits: 23725 },
{ company: "Microsoft Corporation", visits: 1882 },
{ company: "Alphabet Inc. (Google)", visits: 1809 },
{ company: "Amazon.com, Inc.", visits: 1322 },
{ company: "Tesla, Inc.", visits: 1122 },
{ company: "Meta Platforms, Inc. (Facebook)", visits: 1114 },
{ company: "Berkshire Hathaway Inc.", visits: 711 },
{ company: "PepsiCo, Inc.", visits: 443 },
{ company: "Johnson & Johnson Services, Inc.", visits: 350 },
{ company: "McDonald's Corporation", visits: 298 },
{ company: "NVIDIA Corporation", visits: 150 },
{ company: "Netflix, Inc.", visits: 100 }
];
// Создаем ось категорий (ось X)
let categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "company";
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.minGridDistance = 40;
categoryAxis.fontSize = 11;
categoryAxis.renderer.labels.template.wrap = true;
categoryAxis.renderer.labels.template.maxWidth = 80;
categoryAxis.renderer.labels.template.padding(10, 0, 0, 0); // Устанавливаем все стороны в 0
// Создаем ось значений (ось Y)
let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.min = 0;
valueAxis.max = 24000;
valueAxis.strictMinMax = true;
valueAxis.renderer.minGridDistance = 30;
// Создаем серию столбцов
let series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.categoryX = "company";
series.dataFields.valueY = "visits";
series.columns.template.tooltipText = "{valueY.value}";
series.columns.template.tooltipY = 0;
series.columns.template.strokeOpacity = 0;
// Применяем разные цвета к каждому столбцу
series.columns.template.adapter.add("fill", function (fill, target) {
return chart.colors.getIndex(target.dataItem.index);
});
// Добавляем полосу прокрутки
chart.scrollbarX = new am4core.Scrollbar();
chart.scrollbarX.marginTop = 30;
chart.scrollbarX.parent = chart.bottomAxesContainer;
// Добавляем разрыв оси для больших значений
let axisBreak = valueAxis.axisBreaks.create();
axisBreak.startValue = 2100;
axisBreak.endValue = 22900;
// Регулируем размер разрыва оси
let d = (axisBreak.endValue - axisBreak.startValue) / (valueAxis.max - valueAxis.min);
axisBreak.breakSize = 0.05 * (1 - d) / d; // Увеличенный размер разрыва для лучшей видимости
// Регулируем эффект наведения для лучшего взаимодействия
let hoverState = axisBreak.states.create("hover");
hoverState.properties.breakSize = 1; // Немного больший разрыв при наведении
hoverState.properties.opacity = 0.1;
hoverState.transitionDuration = 1500;
axisBreak.defaultState.transitionDuration = 1000;
}); // конец am4core.ready()
Установка отзывчивой максимальной ширины для меток
В amCharts 4 метки для xAxis в столбчатой диаграмме имеют фиксированную maxWidth. Однако, поскольку эта ширина не изменяется динамически, мы должны рассчитывать ее на основе ширины столбца и размера диаграммы.
Для достижения этого мы подключаемся к событиям rangechangeended и sizechanged , проверяем ширину столбцов и устанавливаем maxWidth соответственно.
Реализация
// Функция для обновления максимальной ширины метки на основе самой широкой колонки
const updateLabelMaxWidth = () => {
let maxColumnWidth = 0;
// Проходим по каждой колонке и находим максимальную ширину
series.columns.each(function (column) {
if (column && column.pixelWidth > maxColumnWidth) {
maxColumnWidth = column.pixelWidth;
}
});
// Применяем максимальную ширину к меткам
categoryAxis.renderer.labels.template.maxWidth = maxColumnWidth;
console.log("Обновленная максимальная ширина метки: " + maxColumnWidth);
// Принуждаем метку к повторной обработке
categoryAxis.invalidateLabels();
}
// Запускаем обновление ширины метки при увеличении/панорамировании
categoryAxis.events.on("rangechangeended", function () {
updateLabelMaxWidth();
});
// Запускаем обновление ширины метки при изменении размера экрана
chart.events.on("sizechanged", function () {
updateLabelMaxWidth();
});
Объяснение
Функция updateLabelMaxWidth динамически регулирует ширину меток по оси X на основе самой широкой колонки в диаграмме. Она проходит по всем колонкам, находит максимальную ширину и применяет ее к меткам, чтобы предотвратить наложение.
Хуки событий:
- rangechangeended → Обновляет ширину метки при увеличении/панорамировании.
- sizechanged → Регулирует метки при изменении размера диаграммы.
Это гарантирует, что метки изменяются динамически в зависимости от доступного пространства.
Обрезка длинных меток для лучшей читаемости
Даже с динамической максимальной шириной некоторые метки могут быть слишком длинными и вызывать беспорядок. Чтобы с этим справиться, мы обрезаем метки, когда они превышают определенную длину.
Реализация
categoryAxis.renderer.labels.template.adapter.add("textOutput", function(text, target) {
let maxLabelLength = 10; // Устанавливаем максимальное количество символов перед обрезкой
if (text.length > maxLabelLength) {
return text.substring(0, maxLabelLength) + "..."; // Обрезаем и добавляем многоточие
}
return text; // Возвращаем оригинальный текст, если он в пределах лимита
});
Объяснение
- Эта функция проверяет длину каждой метки.
- Если метка слишком длинная, она обрезает текст до первого слова и добавляет ".." в конце.
- Метки, которые находятся в максимальном лимите символов, остаются без изменений.
Это гарантирует, что длинные метки не накладываются, при этом предоставляя достаточно информации и оставаясь отзывчивыми при изменении размера или увеличении/уменьшении.
Полный код реализации после применения решения
am4core.ready(function () {
// Создаем экземпляр диаграммы
let chart = am4core.create("chartdiv", am4charts.XYChart);
chart.hiddenState.properties.opacity = 0; // Начальное появление
chart.logo.disabled = true;
chart.responsive.enabled = true; // Включаем отзывчивое поведение
// Данные диаграммы
chart.data = [
{ company: "Apple Inc.", visits: 23725 },
{ company: "Microsoft Corporation", visits: 1882 },
{ company: "Alphabet Inc. (Google)", visits: 1809 },
{ company: "Amazon.com, Inc.", visits: 1322 },
{ company: "Tesla, Inc.", visits: 1122 },
{ company: "Meta Platforms, Inc. (Facebook)", visits: 1114 },
{ company: "Berkshire Hathaway Inc.", visits: 711 },
{ company: "PepsiCo, Inc.", visits: 443 },
{ company: "Johnson & Johnson Services, Inc.", visits: 350 },
{ company: "McDonald's Corporation", visits: 298 },
{ company: "NVIDIA Corporation", visits: 150 },
{ company: "Netflix, Inc.", visits: 100 }
];
// Создаем ось категорий (ось X)
let categoryAxis = chart.xAxes.push(new am4charts.CategoryAxis());
categoryAxis.dataFields.category = "company";
categoryAxis.renderer.grid.template.location = 0;
categoryAxis.renderer.minGridDistance = 40;
categoryAxis.fontSize = 11;
categoryAxis.renderer.labels.template.wrap = true;
categoryAxis.renderer.labels.template.maxWidth = 80;
categoryAxis.renderer.labels.template.padding(10, 0, 0, 0); // Устанавливаем все стороны в 0
// Создаем ось значений (ось Y)
let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.min = 0;
valueAxis.max = 24000;
valueAxis.strictMinMax = true;
valueAxis.renderer.minGridDistance = 30;
// Функция для обновления максимальной ширины метки на основе самой широкой колонки
const updateLabelMaxWidth = () => {
let maxColumnWidth = 0;
// Проходим по каждой колонке и находим максимальную ширину
series.columns.each(function (column) {
if (column && column.pixelWidth > maxColumnWidth) {
maxColumnWidth = column.pixelWidth;
}
});
// Применяем максимальную ширину к меткам
categoryAxis.renderer.labels.template.maxWidth = maxColumnWidth;
console.log("Обновленная максимальная ширина метки: " + maxColumnWidth);
// Принуждаем метку к повторной обработке
categoryAxis.invalidateLabels();
}
// Запускаем обновление ширины метки при увеличении/панорамировании
categoryAxis.events.on("rangechangeended", function () {
updateLabelMaxWidth();
});
// Запускаем обновление ширины метки при изменении размера экрана
chart.events.on("sizechanged", function () {
updateLabelMaxWidth();
});
// Обрезаем метки в конце первого слова при необходимости
categoryAxis.renderer.labels.template.adapter.add("textOutput", function (text, target) {
let labelMaxWidth = target.maxWidth;
if (labelMaxWidth < 60 && text) { // Проверяем, меньше ли ширина метки 60 пикселей
let words = text.split(" "); // Разбиваем текст на слова
return words.length > 1 ? words[0] + ".." : text; // Оставляем только первое слово с ".."
}
return text;
});
// Создаем серию столбцов
let series = chart.series.push(new am4charts.ColumnSeries());
series.dataFields.categoryX = "company";
series.dataFields.valueY = "visits";
series.columns.template.tooltipText = "{valueY.value}";
series.columns.template.tooltipY = 0;
series.columns.template.strokeOpacity = 0;
// Применяем разные цвета к каждому столбцу
series.columns.template.adapter.add("fill", function (fill, target) {
return chart.colors.getIndex(target.dataItem.index);
});
// Добавляем полосу прокрутки
chart.scrollbarX = new am4core.Scrollbar();
chart.scrollbarX.marginTop = 30;
chart.scrollbarX.parent = chart.bottomAxesContainer;
// Добавляем разрыв оси для больших значений
let axisBreak = valueAxis.axisBreaks.create();
axisBreak.startValue = 2100;
axisBreak.endValue = 22900;
// Регулируем размер разрыва оси
let d = (axisBreak.endValue - axisBreak.startValue) / (valueAxis.max - valueAxis.min);
axisBreak.breakSize = 0.05 * (1 - d) / d; // Увеличенный размер разрыва для лучшей видимости
// Регулируем эффект наведения для лучшего взаимодействия
let hoverState = axisBreak.states.create("hover");
hoverState.properties.breakSize = 1; // Немного больший разрыв при наведении
hoverState.properties.opacity = 0.1;
hoverState.transitionDuration = 1500;
axisBreak.defaultState.transitionDuration = 1000;
}); // конец am4core.ready()
С этими доработками наша диаграмма теперь отлично адаптируется — метки остаются читаемыми, больше никаких некрасивых наложений, и все плавно изменяется в размере. Динамически регулируя ширину, обрезая длинные названия и подключаясь к событиям, мы убедились, что диаграмма выглядит отлично на любом экране. Теперь попробуйте это сами!