Introduzione

Le etichette nei grafici a barre spesso affrontano problemi di leggibilità quando il grafico viene ridimensionato, visualizzato su diverse risoluzioni dello schermo o ingrandito/ridotto utilizzando i controlli di zoom di amCharts. Per impostazione predefinita, le etichette in amCharts 4 hanno una larghezza fissa, il che può portare a sovrapposizioni, troncamenti o testo illeggibile su schermi piccoli.

Per rendere le etichette veramente reattive, dobbiamo:

  • Calcolare dinamicamente la larghezza massima delle etichette in base alle dimensioni del grafico e alla larghezza delle colonne.
  • Truncare le etichette lunghe se necessario per prevenire il traboccamento.
  • Utilizzare i ganci di evento per garantire che le etichette si adattino correttamente quando:
    • Il grafico viene ridimensionato.
    • Si verifica uno zoom/panning.
    • I dati vengono aggiornati dinamicamente.

In questo articolo, esploreremo come regolare dinamicamente la larghezza delle etichette e truncare le etichette quando necessario utilizzando gli eventi di amCharts 4.

Un esempio di etichette sovrapposte e mancanza di reattività in un grafico a colonne

Codice di implementazione con il problema:


am4core.ready(function () {
    // Crea l'istanza del grafico
    let chart = am4core.create("chartdiv", am4charts.XYChart);
    chart.hiddenState.properties.opacity = 0; // Inizio fade-in
    chart.logo.disabled = true;
    chart.responsive.enabled = true; // Abilita comportamento reattivo

    // Dati del grafico
    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 }
    ];

    // Crea l'asse delle categorie (asse 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); // Imposta tutti i lati a 0

    // Crea l'asse dei valori (asse Y)
    let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
    valueAxis.min = 0;
    valueAxis.max = 24000;
    valueAxis.strictMinMax = true;
    valueAxis.renderer.minGridDistance = 30;

    // Crea la serie di colonne
    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;

    // Applica colori diversi a ciascuna colonna
    series.columns.template.adapter.add("fill", function (fill, target) {
      return chart.colors.getIndex(target.dataItem.index);
    });

    // Aggiungi la barra di scorrimento
    chart.scrollbarX = new am4core.Scrollbar();
    chart.scrollbarX.marginTop = 30;
    chart.scrollbarX.parent = chart.bottomAxesContainer;

    // Aggiungi una rottura dell'asse per valori elevati
    let axisBreak = valueAxis.axisBreaks.create();
    axisBreak.startValue = 2100;
    axisBreak.endValue = 22900;

    // Regola la dimensione della rottura dell'asse
    let d = (axisBreak.endValue - axisBreak.startValue) / (valueAxis.max - valueAxis.min);
    axisBreak.breakSize = 0.05 * (1 - d) / d; // Dimensione della rottura aumentata per una migliore visibilità

    // Regola l'effetto hover per una migliore interazione
    let hoverState = axisBreak.states.create("hover");
    hoverState.properties.breakSize = 1; // Rottura leggermente più grande quando si passa sopra
    hoverState.properties.opacity = 0.1;
    hoverState.transitionDuration = 1500;

    axisBreak.defaultState.transitionDuration = 1000;

}); // fine am4core.ready()

Impostare una larghezza massima reattiva per le etichette

In amCharts 4, le etichette per l'xAxis in un grafico a colonne hanno una larghezza massima fissa. Tuttavia, poiché questa larghezza non si adatta dinamicamente, dobbiamo calcolarla in base alla larghezza della colonna e alle dimensioni del grafico.

Per ottenere questo, ci colleghiamo agli eventi rangechangeendedsizechanged , controlliamo la larghezza delle colonne e impostiamo la larghezza massima di conseguenza.

Implementazione

// Funzione per aggiornare la larghezza massima delle etichette in base alla colonna più larga
const updateLabelMaxWidth = () => {
  let maxColumnWidth = 0;

  // Cicla attraverso ogni colonna e trova la larghezza massima
  series.columns.each(function (column) {
    if (column && column.pixelWidth > maxColumnWidth) {
      maxColumnWidth = column.pixelWidth;
    }
  });

  // Applica la larghezza massima alle etichette
  categoryAxis.renderer.labels.template.maxWidth = maxColumnWidth;
  console.log("Larghezza massima delle etichette aggiornata: " + maxColumnWidth);

  // Forza la rielaborazione delle etichette
  categoryAxis.invalidateLabels();
}

// Attiva l'aggiornamento della larghezza delle etichette su zoom/pan
categoryAxis.events.on("rangechangeended", function () {
  updateLabelMaxWidth();
});

// Attiva l'aggiornamento della larghezza delle etichette su ridimensionamento dello schermo
chart.events.on("sizechanged", function () {
  updateLabelMaxWidth();
});

Spiegazione

La funzione updateLabelMaxWidth regola dinamicamente la larghezza delle etichette dell'asse x in base alla colonna più larga nel grafico. Cicla attraverso tutte le colonne, trova la larghezza massima e la applica alle etichette per prevenire sovrapposizioni.

Ganci di evento:

  • rangechangeended → Aggiorna la larghezza delle etichette durante lo zoom/panning.
  • sizechanged → Regola le etichette quando il grafico viene ridimensionato.

Questo garantisce che le etichette si ridimensionino dinamicamente in base allo spazio disponibile.

Truncare le etichette lunghe per una migliore leggibilità

Anche con una larghezza massima dinamica, alcune etichette possono ancora essere troppo lunghe e causare confusione. Per gestire questo, trunciamo le etichette quando superano una certa lunghezza.

Implementazione

categoryAxis.renderer.labels.template.adapter.add("textOutput", function(text, target) {
    let maxLabelLength = 10; // Imposta il numero massimo di caratteri prima del troncamento

    if (text.length > maxLabelLength) {
        return text.substring(0, maxLabelLength) + "..."; // Trunca e aggiungi ellissi
    }
    return text; // Restituisci il testo originale se è entro il limite
});

Spiegazione

  • Questa funzione controlla la lunghezza di ciascuna etichetta.
  • Se l'etichetta è troppo lunga, tronca il testo alla prima parola e aggiunge ".." alla fine.
  • Le etichette all'interno del limite massimo di caratteri rimangono inalterate.

Questo garantisce che le etichette lunghe non si sovrappongano pur fornendo informazioni sufficienti e rimanendo reattive quando vengono ridimensionate o ingrandite/ridotte.

Risultato finale dopo aver applicato le regolazioni dinamiche delle etichette

Codice di implementazione completo dopo aver applicato la soluzione


am4core.ready(function () {
  // Crea l'istanza del grafico
  let chart = am4core.create("chartdiv", am4charts.XYChart);
  chart.hiddenState.properties.opacity = 0; // Inizio fade-in
  chart.logo.disabled = true;
  chart.responsive.enabled = true; // Abilita comportamento reattivo

  // Dati del grafico
  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 }
  ];

  // Crea l'asse delle categorie (asse 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); // Imposta tutti i lati a 0

  // Crea l'asse dei valori (asse Y)
  let valueAxis = chart.yAxes.push(new am4charts.ValueAxis());
  valueAxis.min = 0;
  valueAxis.max = 24000;
  valueAxis.strictMinMax = true;
  valueAxis.renderer.minGridDistance = 30;

  // Funzione per aggiornare la larghezza massima delle etichette in base alla colonna più larga
  const updateLabelMaxWidth = () => {
    let maxColumnWidth = 0;

    // Cicla attraverso ogni colonna e trova la larghezza massima
    series.columns.each(function (column) {
      if (column && column.pixelWidth > maxColumnWidth) {
        maxColumnWidth = column.pixelWidth;
      }
    });

    // Applica la larghezza massima alle etichette
    categoryAxis.renderer.labels.template.maxWidth = maxColumnWidth;
    console.log("Larghezza massima delle etichette aggiornata: " + maxColumnWidth);

    // Forza la rielaborazione delle etichette
    categoryAxis.invalidateLabels();
  }

  // Attiva l'aggiornamento della larghezza delle etichette su zoom/pan
  categoryAxis.events.on("rangechangeended", function () {
    updateLabelMaxWidth();
  });

  // Attiva l'aggiornamento della larghezza delle etichette su ridimensionamento dello schermo
  chart.events.on("sizechanged", function () {
    updateLabelMaxWidth();
  });

  // Trunca le etichette alla fine della prima parola quando necessario
  categoryAxis.renderer.labels.template.adapter.add("textOutput", function (text, target) {
    let labelMaxWidth = target.maxWidth;

    if (labelMaxWidth < 60 && text) { // Controlla se la larghezza dell'etichetta è inferiore a 60 pixel
      let words = text.split(" "); // Dividi il testo in parole
      return words.length > 1 ? words[0] + ".." : text; // Mantieni solo la prima parola con ".."
    }

    return text;
  });

  // Crea la serie di colonne
  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;

  // Applica colori diversi a ciascuna colonna
  series.columns.template.adapter.add("fill", function (fill, target) {
    return chart.colors.getIndex(target.dataItem.index);
  });

  // Aggiungi la barra di scorrimento
  chart.scrollbarX = new am4core.Scrollbar();
  chart.scrollbarX.marginTop = 30;
  chart.scrollbarX.parent = chart.bottomAxesContainer;

  // Aggiungi una rottura dell'asse per valori elevati
  let axisBreak = valueAxis.axisBreaks.create();
  axisBreak.startValue = 2100;
  axisBreak.endValue = 22900;

  // Regola la dimensione della rottura dell'asse
  let d = (axisBreak.endValue - axisBreak.startValue) / (valueAxis.max - valueAxis.min);
  axisBreak.breakSize = 0.05 * (1 - d) / d; // Dimensione della rottura aumentata per una migliore visibilità

  // Regola l'effetto hover per una migliore interazione
  let hoverState = axisBreak.states.create("hover");
  hoverState.properties.breakSize = 1; // Rottura leggermente più grande quando si passa sopra
  hoverState.properties.opacity = 0.1;
  hoverState.transitionDuration = 1500;

  axisBreak.defaultState.transitionDuration = 1000;

}); // fine am4core.ready()

Con queste modifiche, il nostro grafico ora si adatta perfettamente—le etichette rimangono leggibili, niente più brutte sovrapposizioni, e tutto si ridimensiona senza problemi. Regolando dinamicamente le larghezze, troncando i nomi lunghi e collegandoci agli eventi, ci siamo assicurati che il grafico abbia un aspetto fantastico su qualsiasi schermo. Ora, prova a farlo tu stesso!