ChartJS 版本 3 - 多个折线图的常用图例

Posted

技术标签:

【中文标题】ChartJS 版本 3 - 多个折线图的常用图例【英文标题】:ChartJS Version 3 - common legend for multiple line charts 【发布时间】:2021-12-12 06:03:34 【问题描述】:

我想为多个图表使用一个共同的图例,我遇到了这篇文章Chart.js Multiple charts with one common legend,它可能适用于低于 3 的 ChartJS 版本,但是我没有尝试过,因为我使用的是 ChartJS 版本 3。

给定的解决方案不适用于 ChartJS 版本 > 3,因为版本 3 中不推荐使用少数 API 函数 - Chart.prototype.generateLegend

有人可以帮助如何使用 ChartJS 版本 3(最新版本)实现这一目标。这是我正在寻找的非常重要的功能之一。

【问题讨论】:

【参考方案1】:

您可以使用自定义插件生成图例并将点击处理程序绑定到所有图表,在我当前的实现中,您必须将所有图表放在一个数组中才能工作:

const getOrCreateLegendList = (chart, id) => 
  const legendContainer = document.getElementById(id);
  let listContainer = legendContainer.querySelector('ul');

  if (!listContainer) 
    listContainer = document.createElement('ul');
    listContainer.style.display = 'flex';
    listContainer.style.flexDirection = 'row';
    listContainer.style.margin = 0;
    listContainer.style.padding = 0;

    legendContainer.appendChild(listContainer);
  

  return listContainer;
;

const htmlLegendPlugin = 
  id: 'htmlLegend',
  afterUpdate(chart, args, options) 
    const ul = getOrCreateLegendList(chart, options.containerID);

    // Remove old legend items
    while (ul.firstChild) 
      ul.firstChild.remove();
    

    // Reuse the built-in legendItems generator
    const items = chart.options.plugins.legend.labels.generateLabels(chart);

    items.forEach(item => 
      const li = document.createElement('li');
      li.style.alignItems = 'center';
      li.style.cursor = 'pointer';
      li.style.display = 'flex';
      li.style.flexDirection = 'row';
      li.style.marginLeft = '10px';

      li.onclick = () => 
        const 
          type
         = chart.config;
        if (type === 'pie' || type === 'doughnut') 
          // Pie and doughnut charts only have a single dataset and visibility is per item
          charts.forEach((chartItem) => 
            chartItem.toggleDataVisibility(item.index);
            chartItem.update();
          )
         else 
          charts.forEach((chartItem) => 
            chartItem.setDatasetVisibility(item.datasetIndex, !chartItem.isDatasetVisible(item.datasetIndex));
            chartItem.update();
          )
        
      ;

      // Color box
      const boxSpan = document.createElement('span');
      boxSpan.style.background = item.fillStyle;
      boxSpan.style.borderColor = item.strokeStyle;
      boxSpan.style.borderWidth = item.lineWidth + 'px';
      boxSpan.style.display = 'inline-block';
      boxSpan.style.height = '20px';
      boxSpan.style.marginRight = '10px';
      boxSpan.style.width = '20px';

      // Text
      const textContainer = document.createElement('p');
      textContainer.style.color = item.fontColor;
      textContainer.style.margin = 0;
      textContainer.style.padding = 0;
      textContainer.style.textDecoration = item.hidden ? 'line-through' : '';

      const text = document.createTextNode(item.text);
      textContainer.appendChild(text);

      li.appendChild(boxSpan);
      li.appendChild(textContainer);
      ul.appendChild(li);
    );
  
;

const options = 
  type: 'line',
  data: 
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [
        label: '# of Votes',
        data: [12, 19, 3, 5, 2, 3],
        borderColor: 'pink',
        backgroundColor: 'pink',
      ,
      
        label: '# of Points',
        data: [7, 11, 5, 8, 3, 7],
        borderColor: 'yellow',
        backgroundColor: 'yellow',
      
    ]
  ,
  options: 
    plugins: 
      legend: 
        display: false,
      ,
      htmlLegend: 
        // ID of the container to put the legend in
        containerID: 'chartLegend',
      
    
  ,
  plugins: [htmlLegendPlugin]


const options2 = 
  type: 'line',
  data: 
    labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
    datasets: [
        label: '# of Votes',
        data: [4, 9, 13, 15, 12, 6],
        borderColor: 'pink',
        backgroundColor: 'pink',
      ,
      
        label: '# of Points',
        data: [17, 1, 15, 18, 9, 3],
        borderColor: 'yellow',
        backgroundColor: 'yellow',
      
    ]
  ,
  options: 
    plugins: 
      legend: 
        display: false
      
    
  


const ctx = document.getElementById('chartJSContainer').getContext('2d');
const ctx2 = document.getElementById('chartJSContainer2').getContext('2d');

const chart = new Chart(ctx, options);
const chart2 = new Chart(ctx2, options2);

const charts = [chart, chart2];
<body>
  <div id="chartLegend">

  </div>
  <canvas id="chartJSContainer"  ></canvas>
  <canvas id="chartJSContainer2"  ></canvas>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.5.0/chart.js"></script>
</body>

【讨论】:

谢谢,这很有效,但与chartsjs v2 实现相比,它的复杂程度有点疯狂。

以上是关于ChartJS 版本 3 - 多个折线图的常用图例的主要内容,如果未能解决你的问题,请参考以下文章

ecahrt折线图图例换页展示

echarts折线图tooltip中图例颜色对应混乱问题

Python使用matplotlib函数subplot可视化多个不同颜色的折线图为指定的子图添加图例信息(legend)

Chartjs 简单使用 ------ 制作sin cos 折线图

ChartJS - 标签背景

如何在折线图js的多个数据集中只显示一行