Introducción
Las etiquetas en gráficos de barras a menudo enfrentan problemas de legibilidad cuando el gráfico se redimensiona, se muestra en diferentes resoluciones de pantalla o se hace zoom utilizando los controles de zoom de amCharts. Por defecto, las etiquetas en amCharts 4 tienen un ancho fijo, lo que puede llevar a superposición, truncamiento o texto ilegible en pantallas pequeñas.
Para hacer que las etiquetas sean realmente responsivas, necesitamos:
- Calcular dinámicamente el ancho máximo de las etiquetas basado en el tamaño del gráfico y el ancho de la columna.
- Truncar etiquetas largas si es necesario para evitar desbordamientos.
- Usar ganchos de eventos para asegurar que las etiquetas se ajusten correctamente cuando:
- El gráfico se redimensiona.
- Se produce zoom/desplazamiento.
- Los datos se actualizan dinámicamente.
En este artículo, exploraremos cómo ajustar dinámicamente el ancho de las etiquetas y truncar etiquetas cuando sea necesario utilizando eventos de amCharts 4.
Código de implementación con el problema:
am4core.ready(function () {
// Crear la instancia del gráfico
let chart = am4core.create("chartdiv", am4charts.XYChart);
chart.hiddenState.properties.opacity = 0; // Desvanecimiento inicial
chart.logo.disabled = true;
chart.responsive.enabled = true; // Habilitar comportamiento responsivo
// Datos del gráfico
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 }
];
// Crear Eje de Categoría (Eje 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); // Establecer todos los lados a 0
// Crear Eje de Valor (Eje Y)
let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.min = 0;
valueAxis.max = 24000;
valueAxis.strictMinMax = true;
valueAxis.renderer.minGridDistance = 30;
// Crear Serie de Columnas
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;
// Aplicar diferentes colores a cada columna
series.columns.template.adapter.add("fill", function (fill, target) {
return chart.colors.getIndex(target.dataItem.index);
});
// Agregar Barra de Desplazamiento
chart.scrollbarX = new am4core.Scrollbar();
chart.scrollbarX.marginTop = 30;
chart.scrollbarX.parent = chart.bottomAxesContainer;
// Agregar Ruptura de Eje para valores grandes
let axisBreak = valueAxis.axisBreaks.create();
axisBreak.startValue = 2100;
axisBreak.endValue = 22900;
// Ajustar Tamaño de Ruptura de Eje
let d = (axisBreak.endValue - axisBreak.startValue) / (valueAxis.max - valueAxis.min);
axisBreak.breakSize = 0.05 * (1 - d) / d; // Aumentar tamaño de ruptura para mejor visibilidad
// Ajustar efecto de hover para mejor interacción
let hoverState = axisBreak.states.create("hover");
hoverState.properties.breakSize = 1; // Ruptura ligeramente más grande al pasar el mouse
hoverState.properties.opacity = 0.1;
hoverState.transitionDuration = 1500;
axisBreak.defaultState.transitionDuration = 1000;
}); // fin am4core.ready()
Estableciendo un Ancho Máximo Responsivo para las Etiquetas
En amCharts 4, las etiquetas para el xAxis en un gráfico de columnas tienen un maxWidth fijo. Sin embargo, dado que este ancho no se ajusta dinámicamente, debemos calcularlo en función del ancho de la columna y el tamaño del gráfico.
Para lograr esto, nos enganchamos en el evento rangechangeended y sizechanged , verificamos el ancho de las columnas y establecemos el maxWidth en consecuencia.
Implementación
// Función para actualizar el ancho máximo de la etiqueta basado en la columna más ancha
const updateLabelMaxWidth = () => {
let maxColumnWidth = 0;
// Recorrer cada columna y encontrar el ancho máximo
series.columns.each(function (column) {
if (column && column.pixelWidth > maxColumnWidth) {
maxColumnWidth = column.pixelWidth;
}
});
// Aplicar ancho máximo a las etiquetas
categoryAxis.renderer.labels.template.maxWidth = maxColumnWidth;
console.log("Ancho máximo de etiqueta actualizado: " + maxColumnWidth);
// Forzar a que la etiqueta se reprocesa
categoryAxis.invalidateLabels();
}
// Disparar actualización del ancho de la etiqueta en zoom/desplazamiento
categoryAxis.events.on("rangechangeended", function () {
updateLabelMaxWidth();
});
// Disparar actualización del ancho de la etiqueta en redimensionamiento de pantalla
chart.events.on("sizechanged", function () {
updateLabelMaxWidth();
});
Explicación
La función updateLabelMaxWidth ajusta dinámicamente el ancho de las etiquetas del eje x basado en la columna más ancha en el gráfico. Recorre todas las columnas, encuentra el ancho máximo y lo aplica a las etiquetas para prevenir superposición.
Ganchos de Eventos:
- rangechangeended → Actualiza el ancho de la etiqueta al hacer zoom/desplazamiento.
- sizechanged → Ajusta las etiquetas cuando el gráfico se redimensiona.
Esto asegura que las etiquetas se redimensionen dinámicamente según el espacio disponible.
Truncando Etiquetas Largas para Mejor Legibilidad
Incluso con un ancho máximo dinámico, algunas etiquetas pueden seguir siendo demasiado largas y causar desorden. Para manejar esto, truncamos etiquetas cuando exceden cierta longitud.
Implementación
categoryAxis.renderer.labels.template.adapter.add("textOutput", function(text, target) {
let maxLabelLength = 10; // Establecer el número máximo de caracteres antes de truncar
if (text.length > maxLabelLength) {
return text.substring(0, maxLabelLength) + "..."; // Truncar y agregar puntos suspensivos
}
return text; // Devolver el texto original si está dentro del límite
});
Explicación
- Esta función verifica la longitud de cada etiqueta.
- Si la etiqueta es demasiado larga, trunca el texto al primer palabra y agrega ".." al final.
- Las etiquetas dentro del límite máximo de caracteres permanecen sin cambios.
Esto asegura que las etiquetas largas no se superpongan mientras aún proporcionan suficiente información y se mantienen responsivas al ser redimensionadas o al hacer zoom.
Código de Implementación Completo después de aplicar la solución
am4core.ready(function () {
// Crear la instancia del gráfico
let chart = am4core.create("chartdiv", am4charts.XYChart);
chart.hiddenState.properties.opacity = 0; // Desvanecimiento inicial
chart.logo.disabled = true;
chart.responsive.enabled = true; // Habilitar comportamiento responsivo
// Datos del gráfico
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 }
];
// Crear Eje de Categoría (Eje 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); // Establecer todos los lados a 0
// Crear Eje de Valor (Eje Y)
let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
valueAxis.min = 0;
valueAxis.max = 24000;
valueAxis.strictMinMax = true;
valueAxis.renderer.minGridDistance = 30;
// Función para actualizar el ancho máximo de la etiqueta basado en la columna más ancha
const updateLabelMaxWidth = () => {
let maxColumnWidth = 0;
// Recorrer cada columna y encontrar el ancho máximo
series.columns.each(function (column) {
if (column && column.pixelWidth > maxColumnWidth) {
maxColumnWidth = column.pixelWidth;
}
});
// Aplicar ancho máximo a las etiquetas
categoryAxis.renderer.labels.template.maxWidth = maxColumnWidth;
console.log("Ancho máximo de etiqueta actualizado: " + maxColumnWidth);
// Forzar a que la etiqueta se reprocesa
categoryAxis.invalidateLabels();
}
// Disparar actualización del ancho de la etiqueta en zoom/desplazamiento
categoryAxis.events.on("rangechangeended", function () {
updateLabelMaxWidth();
});
// Disparar actualización del ancho de la etiqueta en redimensionamiento de pantalla
chart.events.on("sizechanged", function () {
updateLabelMaxWidth();
});
// Truncar etiquetas al final de la primera palabra cuando sea necesario
categoryAxis.renderer.labels.template.adapter.add("textOutput", function (text, target) {
let labelMaxWidth = target.maxWidth;
if (labelMaxWidth < 60 && text) { // Verificar si el ancho de la etiqueta es menor a 60 píxeles
let words = text.split(" "); // Dividir el texto en palabras
return words.length > 1 ? words[0] + ".." : text; // Mantener solo la primera palabra con ".."
}
return text;
});
// Crear Serie de Columnas
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;
// Aplicar diferentes colores a cada columna
series.columns.template.adapter.add("fill", function (fill, target) {
return chart.colors.getIndex(target.dataItem.index);
});
// Agregar Barra de Desplazamiento
chart.scrollbarX = new am4core.Scrollbar();
chart.scrollbarX.marginTop = 30;
chart.scrollbarX.parent = chart.bottomAxesContainer;
// Agregar Ruptura de Eje para valores grandes
let axisBreak = valueAxis.axisBreaks.create();
axisBreak.startValue = 2100;
axisBreak.endValue = 22900;
// Ajustar Tamaño de Ruptura de Eje
let d = (axisBreak.endValue - axisBreak.startValue) / (valueAxis.max - valueAxis.min);
axisBreak.breakSize = 0.05 * (1 - d) / d; // Aumentar tamaño de ruptura para mejor visibilidad
// Ajustar efecto de hover para mejor interacción
let hoverState = axisBreak.states.create("hover");
hoverState.properties.breakSize = 1; // Ruptura ligeramente más grande al pasar el mouse
hoverState.properties.opacity = 0.1;
hoverState.transitionDuration = 1500;
axisBreak.defaultState.transitionDuration = 1000;
}); // fin am4core.ready()
Con estos ajustes, nuestro gráfico ahora se adapta perfectamente—las etiquetas se mantienen legibles, no más superposiciones feas, y todo se redimensiona suavemente. Al ajustar dinámicamente los anchos, truncar nombres largos y engancharse a eventos, hemos asegurado que el gráfico se vea genial en cualquier pantalla. ¡Ahora, adelante y pruébalo!