Gráficos Excel mais claros em JavaScript: rótulos multinível e eixos duplos

2026-09-23 08:01:42 Allen Yang
AI Summarize:
ChatGPT
ChatGPT ✓
Claude ✓
Grok ✓
Perplexity ✓
Quick
Quick
Concise overview
Highlights
Key takeaways
Detailed
Structured explanation
Brief
One sentence summary
Summarize |

Creating multi-level category labels and adding a secondary axis to an Excel chart in the browser with Spire.XLS for JavaScript

Um gráfico de colunas com regiões e meses no mesmo eixo apresenta dois problemas, e eles não são o mesmo problema. O primeiro é que os rótulos de categoria se aglutinam em uma única linha — "Norte", "Jan", "Norte", "Fev" — e o leitor precisa reagrupar mentalmente qual mês pertence a qual região. O segundo é que, quando uma série de taxa de crescimento é adicionada ao lado de uma série de vendas que chega aos milhões, a taxa de crescimento se torna uma linha plana colada à linha de base, porque um único eixo de valores não consegue atender a duas ordens de grandeza ao mesmo tempo.

Os rótulos de categoria de vários níveis resolvem o primeiro problema. Um eixo secundário resolve o segundo. São recursos independentes que, por acaso, são úteis no mesmo gráfico, e o Spire.XLS for JavaScript lida com ambos por meio da API de eixos do gráfico — diretamente no navegador via WebAssembly, com os arquivos passando por um sistema de arquivos virtual (VFS) e sem envolvimento de backend.

Para a configuração do projeto, consulte Integrating Spire.XLS for JavaScript in a React Project. Os exemplos abaixo pressupõem que o pacote está instalado e que o módulo WebAssembly foi inicializado.


Quando um eixo não é suficiente

Os dois problemas aparecem no mesmo tipo de planilha — uma em que as categorias têm uma hierarquia e os valores têm uma dispersão — mas vêm de lugares diferentes:

Problema De onde vem Como fica o gráfico O que resolve
Os rótulos se acumulam em uma linha As categorias são hierárquicas (região → mês, ano → trimestre), mas o eixo as trata como planas Uma única linha de rótulos em que as categorias externas e internas se alternam sem agrupamento visual Rótulos de categoria de vários níveis
Uma série se achata em uma linha Duas séries diferem em ordens de grandeza (vendas em milhões, crescimento em porcentagem), mas compartilham um único eixo de valores A série menor se comprime para perto de zero e sua variação fica invisível Eixo secundário

Nenhum dos dois é uma questão de estilo. Ambos se devem ao fato de o eixo não saber algo que precisa saber — que as categorias têm camadas, ou que os valores têm escalas incompatíveis. As duas seções abaixo abordam cada um deles por vez, e a segunda se baseia na primeira, de modo que o gráfico final carrega ambas as correções.


Pré-requisitos

Você precisa de um projeto React com o Spire.XLS for JavaScript instalado e o módulo WebAssembly inicializado, acessível em window.wasmModule.spirexls. O exemplo carrega uma fonte e um arquivo de dados pré-construído no VFS antes de criar o gráfico, e ambos são obtidos da pasta public do projeto.


Os dados por trás dos rótulos de vários níveis

Os rótulos de vários níveis não são criados apenas por uma propriedade — eles são lidos dos dados. O eixo de categorias desenha tantos níveis de rótulos quantas forem as colunas no intervalo para o qual CategoryLabels aponta. Portanto, a planilha precisa ser disposta com a hierarquia distribuída pelas colunas:

Coluna A (externa) Coluna B (interna) Coluna C (valores)
Norte Jan 120.000
Norte Fev 135.000
Sul Jan 98.000
Sul Fev 110.000

Os rótulos externos na coluna A estão mesclados ao longo das linhas que abrangem — "Norte" abrange as duas linhas de Jan e Fev. Essa mesclagem é o que faz o nível se aglutinar visualmente em um único rótulo por grupo quando o gráfico é renderizado. Sem ela, o eixo ainda mostra dois níveis, mas o nível externo repete o rótulo em cada linha em vez de agrupá-lo.

Isso é uma questão de layout de dados, não de API de gráfico. O código do gráfico só precisa apontar CategoryLabels para ambas as colunas; se as células externas estão mescladas é decidido na pasta de trabalho, não no objeto de gráfico.


Criar um gráfico com rótulos de categoria de vários níveis

Depois que os dados estão dispostos, o código do gráfico faz duas coisas: aponta CategoryLabels para um intervalo que abrange tanto a coluna externa quanto a interna, e ativa MultiLevelLable para que o eixo expanda essas colunas em linhas empilhadas. As etapas são:

  1. Carregar a fonte e o arquivo de dados de teste no VFS.
  2. Carregar a pasta de trabalho e obter a planilha.
  3. Adicionar um gráfico de colunas e adicionar uma série de vendas nomeada.
  4. Apontar os rótulos de categoria para a coluna de região e a de mês.
  5. Ativar os rótulos de vários níveis para o eixo de categorias e salvar a pasta de trabalho.
function App() {
  const createMultiLevelChart = async () => {
    // Get the Spire.XLS WASM module
    const xlsModule = window.wasmModule?.spirexls;

    // Check if the module is ready
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // Load the font and the test data file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'MultiLevelChartData.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // Load the workbook and get the first worksheet
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });
    const sheet = workbook.Worksheets.get(0);

    // Add a column chart
    const chart = sheet.Charts.Add({ chartType: xlsModule.ExcelChartType.ColumnClustered });
    chart.ChartTitle = "Sales";
    chart.Legend.Delete();

    // Add the sales series and give it a name
    const serie = chart.Series.Add({ name: "Sales", serieType: xlsModule.ExcelChartType.ColumnClustered });
    serie.Values = sheet.Range.get("C2:C7");

    // Point the category labels at both the region and the month column
    serie.CategoryLabels = sheet.Range.get("A2:B7");

    // Turn on multi-level category labels so each level gets its own row
    chart.PrimaryCategoryAxis.MultiLevelLable = true;

    // Place the chart on the worksheet
    chart.LeftColumn = 5;
    chart.TopRow = 1;
    chart.RightColumn = 14;

    // Save the workbook
    const outputFileName = "MultiLevelLabels.xlsx";
    workbook.SaveToFile({ fileName: outputFileName });

    // Dispose of the workbook object to free resources
    workbook.Dispose();

    // Read the result file from the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Multi-Level Labels</h1>
      <button onClick={createMultiLevelChart}>Start</button>
    </div>
  );
}

export default App;

Um gráfico com rótulos de categoria de vários níveis, cada nível em sua própria linha

Create a chart with multi-level category labels

O intervalo A2:B7 é o que faz o eixo mostrar dois níveis. Vincular um intervalo de uma única coluna, como B2:B7, ainda produziria um nível mesmo com MultiLevelLable definido como true — a propriedade controla se vários níveis são expandidos em linhas, não se existe um nível de dados a expandir.


Por que a série de crescimento desaparece

Adicione uma segunda série para o crescimento ano a ano — valores na casa dos dez, em porcentagens — e plote-a no mesmo eixo de valores das vendas. As colunas de vendas chegam a 120.000; a taxa de crescimento chega a 12. Em um eixo que varia de 0 a 140.000, o número 12 é indistinguível de zero. A série está lá, os dados estão corretos, e o gráfico mostra uma linha plana colada à linha de base.

Isso não é um bug nos dados nem no gráfico. É o eixo de valores fazendo seu trabalho — mapear um intervalo que cobre a maior série — ao custo da menor. A única maneira de ver as duas séries com clareza é dar a cada uma sua própria escala, e é isso que o eixo secundário faz.


Mover uma série para o eixo secundário

A série de crescimento é adicionada como linha em vez de coluna. Uma linha não ocupa largura de barra, então ela se destaca claramente em relação à série de colunas que compartilha as mesmas categorias. Movê-la para fora do eixo primário é uma única propriedade: UsePrimaryAxis = false. As etapas são:

  1. Carregar a fonte e o arquivo de dados de teste no VFS.
  2. Carregar a pasta de trabalho e obter a planilha.
  3. Adicionar um gráfico de colunas e adicionar uma série de vendas nomeada.
  4. Adicionar a série de crescimento como linha.
  5. Mover a série de crescimento para o eixo secundário e salvar a pasta de trabalho.
function App() {
  const addSecondaryAxis = async () => {
    // Get the Spire.XLS WASM module
    const xlsModule = window.wasmModule?.spirexls;

    // Check if the module is ready
    if (!xlsModule) {
      alert('Spire.Xls is not ready yet');
      return;
    }

    // Load the font and the test data file into the VFS
    await window.spire.FetchFileToVFS('ARIAL.TTF', '/Library/Fonts/', `${process.env.PUBLIC_URL}/font/`);
    const inputFileName = 'MultiLevelChartData.xlsx';
    await window.spire.FetchFileToVFS(inputFileName, '', `${process.env.PUBLIC_URL}data/`);

    // Load the workbook and get the first worksheet
    const workbook = new xlsModule.Workbook();
    workbook.LoadFromFile({ fileName: inputFileName });
    const sheet = workbook.Worksheets.get(0);

    // Add a column chart
    const chart = sheet.Charts.Add({ chartType: xlsModule.ExcelChartType.ColumnClustered });
    chart.ChartTitle = "Sales and YoY Growth";

    // Add the sales series, which stays on the primary axis
    const salesSerie = chart.Series.Add({ name: "Sales", serieType: xlsModule.ExcelChartType.ColumnClustered });
    salesSerie.Values = sheet.Range.get("C2:C7");

    // Point the category labels at both the region and the month column
    salesSerie.CategoryLabels = sheet.Range.get("A2:B7");

    // Add the growth series as a line
    const growthSerie = chart.Series.Add({ name: "YoY Growth", serieType: xlsModule.ExcelChartType.Line });
    growthSerie.Values = sheet.Range.get("D2:D7");

    // Move the growth series to the secondary axis so it plots on its own percentage scale
    growthSerie.UsePrimaryAxis = false;

    // Turn on multi-level category labels
    chart.PrimaryCategoryAxis.MultiLevelLable = true;

    // Place the chart on the worksheet
    chart.LeftColumn = 5;
    chart.TopRow = 1;
    chart.RightColumn = 14;

    // Save the workbook
    const outputFileName = "SecondaryAxis.xlsx";
    workbook.SaveToFile({ fileName: outputFileName });

    // Dispose of the workbook object to free resources
    workbook.Dispose();

    // Read the result file from the VFS and trigger the download
    const fileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
    const blob = new Blob([fileArray], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = outputFileName;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div style={{ textAlign: 'center', height: '300px' }}>
      <h1>Secondary Axis</h1>
      <button onClick={addSecondaryAxis}>Start</button>
    </div>
  );
}

export default App;

Um gráfico de colunas com um eixo secundário para a série de linhas da taxa de crescimento

Add a secondary axis to the chart

UsePrimaryAxis = false afeta apenas a série na qual é definido; todas as outras séries permanecem no eixo primário. O gráfico ganha um segundo par de eixos de valores e de categorias, dando-lhe dois intervalos de escala separados. Series.Add recebe o nome da série ao mesmo tempo, então a legenda mostra o nome passado em vez de um "Série 1" gerado automaticamente.


Definir a escala do eixo secundário

Quando uma série é movida para o eixo secundário, esse eixo calcula sua própria escala — e o faz de forma independente do primário. Os dois intervalos não têm conhecimento um do outro, o que significa que o eixo secundário pode escolher limites que não se alinham bem com os dados.

PrimaryValueAxis.MinValue, MaxValue e MajorUnit controlam apenas o eixo primário. Para definir a escala do eixo secundário, use SecondaryValueAxis:

// Give the secondary axis a 0-20 scale with a major unit of 5
chart.SecondaryValueAxis.MinValue = 0;
chart.SecondaryValueAxis.MaxValue = 20;
chart.SecondaryValueAxis.MajorUnit = 5;

Defina a escala depois que a série tiver sido movida para o eixo secundário. Enquanto nenhuma série usar o eixo secundário, a atribuição é aceita mas nunca gravada no arquivo — o eixo não existe na saída até que uma série seja plotada nele.


Problemas comuns

O eixo de categorias mostra apenas um nível de rótulos. O CategoryLabels está apontando para um intervalo de uma única coluna. O número de níveis é decidido por quantas colunas o intervalo abrange, não pela propriedade MultiLevelLable. Aponte para um intervalo de várias colunas, como A2:B7, e certifique-se de que as células de rótulo externas estejam mescladas nos dados.

A escala do eixo secundário parece errada. Os eixos de valores primário e secundário calculam suas escalas de forma independente. Definir MinValue ou MaxValue em PrimaryValueAxis não afeta o eixo secundário. Use chart.SecondaryValueAxis para definir sua escala diretamente, e faça isso depois de mover uma série para ele.

A série de crescimento ainda aparece plana depois de adicionar um eixo secundário. Verifique se UsePrimaryAxis = false está definido na série de crescimento, não na série de vendas. A propriedade é por série — defini-la na série errada move a série errada para o eixo secundário.

A legenda mostra "Série 1" em vez do nome da série. O nome não foi passado para Series.Add. Use chart.Series.Add({ name: "Sales", ... }) para que a legenda use o nome pretendido em vez de um rótulo gerado automaticamente.


Perguntas frequentes

Posso ter mais de dois níveis de rótulos de categoria?

Sim. O número de níveis é determinado pelo número de colunas que o intervalo CategoryLabels abrange. Um intervalo de três colunas produz três níveis — por exemplo, ano, trimestre e mês. As células de rótulo externas precisam estar mescladas nos dados para que cada nível seja agrupado corretamente.

O eixo secundário funciona com tipos de gráfico diferentes de colunas e linhas?

Sim. O eixo secundário não está vinculado a um tipo específico de gráfico. O padrão comum é colunas mais linha — a linha não ocupa largura de barra e se destaca claramente em relação às colunas — mas qualquer série pode ser movida para o eixo secundário definindo UsePrimaryAxis = false.

Preciso ter o Excel instalado para criar esses gráficos?

Não. O mecanismo de planilha vem com o pacote e é executado como WebAssembly no navegador. A pasta de trabalho é criada, recebe o gráfico e é salva inteiramente no lado do cliente.

Posso controlar o eixo de categorias secundário separadamente?

Quando uma série é movida para o eixo secundário, o gráfico ganha um eixo de categorias secundário além do eixo de valores secundário. Os dois eixos de categorias compartilham os mesmos rótulos de categoria por padrão, então os rótulos de vários níveis se aplicam a ambos.

O arquivo de saída é compatível com o Excel?

Sim. A pasta de trabalho é salva como .xlsx, e o gráfico — incluindo os rótulos de vários níveis e o eixo secundário — é gravado como XML de gráfico padrão que o Excel lê nativamente.


Veja também