首页 > 解决方案 > 同一组数据的两个图表 - Chart.js

问题描述

是否可以为同一个数据集获得 2 种不同类型的图表(在本例中为条形和线形),但在一个标签下?

例如,这是我的图表:

在此处输入图像描述

const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());

function getConfig() {
  return {
    type: 'bar',
    data: {
      labels: [ "Mar-20", "Apr-20" ],
      datasets: [{
        type: "line",
        label: "en-US",
        borderColor: "#8c856f",
        data: [ 2, 3 ],
        borderWidth: 2,
        fill: false
      }, {
        type: "bar",
        label: "en-US",
        backgroundColor: "#beb391",
        data: [ 2, 3 ]
      }, {
        type: "line",
        label: "sv-SE",
        borderColor: "#b3cbaa",
        data: [ 1, 2 ],
        borderWidth: 2,
        fill: false
      }, {
        type: "bar",
        label: "sv-SE",
        backgroundColor: "#683e3a",
        data: [ 1, 2 ]
      }]
    },
    options: {
      scales: {
        yAxes: [{
          ticks: {
            min : 0
          }
        }]
      }
    }
  };
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart-container"></canvas>

如您所见,我有 4 个数据集:每种语言 2 个(en & se)。除了为每个数据集生成图例标签(2 x“en-US”,2 x“sv-SE”)之外,这一切都工作得很好,而我只想显示唯一的标签,基本上使用主要的值“标签”属性,而不是每个数据集中的那个。

有可能吗,我该怎么做?

标签: javascriptchart.js

解决方案


您可以通过以下方式过滤掉线系列的标签:

options: {
  legend: {
    labels: {
      filter: function(item, chart) {
        return chart.datasets[item.datasetIndex].type === 'bar';
      }
    },
    onClick: function(e, legendItem) {
      let chart = this.chart;
      let index = legendItem.datasetIndex;
      let visible = !chart.getDatasetMeta(index).hidden;
      chart.data.datasets.forEach((dataset, i) => {
        if (dataset.label === legendItem.text) {
          chart.getDatasetMeta(i).hidden = visible;
        }
      });
      chart.update();
    }
  }
}

这已改编自:在chartjs中是否可以隐藏某些数据集图例?

为了切换相似的数据集,我遵循了这个例子


演示

const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());

function getConfig() {
  return {
    type: 'bar',
    data: {
      labels: ["Mar-20", "Apr-20"],
      datasets: [{
        type: "line",
        label: "en-US",
        borderColor: "#8c856f",
        data: [2, 3],
        borderWidth: 2,
        fill: false
      }, {
        type: "bar",
        label: "en-US",
        backgroundColor: "#beb391",
        data: [2, 3]
      }, {
        type: "line",
        label: "sv-SE",
        borderColor: "#b3cbaa",
        data: [1, 2],
        borderWidth: 2,
        fill: false
      }, {
        type: "bar",
        label: "sv-SE",
        backgroundColor: "#683e3a",
        data: [1, 2]
      }]
    },
    options: {
      legend: {
        labels: {
          filter: function(item, chart) {
            return chart.datasets[item.datasetIndex].type === 'bar';
          }
        },
        // Override hide/show for multiple series with same label.
        onClick: function(e, legendItem) {
          let chart = this.chart;
          let index = legendItem.datasetIndex;
          let visible = !chart.getDatasetMeta(index).hidden;
          chart.data.datasets.forEach((dataset, i) => {
            if (dataset.label === legendItem.text) {
              chart.getDatasetMeta(i).hidden = visible;
            }
          });
          chart.update();
        }
      },
      scales: {
        yAxes: [{
          ticks: {
            min: 0
          }
        }]
      }
    }
  };
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.js"></script>
<canvas id="chart-container"></canvas>


或者,您可以创建一个插件......这样您就可以配置每个单独的系列。

var hideLegendItemsPlugin = {
  beforeInit: function(chartInstance) {
    if (chartInstance.options.hideLegendItemsPlugin) {
      Object.assign(chartInstance.options.legend, {
        labels : {
          filter : (item, chart) => {
            return chart.datasets[item.datasetIndex].hideLegendItem !== true;
          }
        },
        onClick: function(e, legendItem) {
          let chart = this.chart;
          let index = legendItem.datasetIndex;
          let visible = !chart.getDatasetMeta(index).hidden;
          chart.data.datasets.forEach((dataset, i) => {
            if (dataset.label === legendItem.text) {
              chart.getDatasetMeta(i).hidden = visible;
            }
          });
          chart.update();
        }
      });
    }
  }
};
Chart.pluginService.register(hideLegendItemsPlugin);

const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());

function getConfig() {
  return {
    type: 'bar',
    data: {
      labels: ["Mar-20", "Apr-20"],
      datasets: [{
        type: "line",
        label: "en-US",
        borderColor: "#8c856f",
        data: [2, 3],
        borderWidth: 2,
        fill: false,
        hideLegendItem: true // Hide it!
      }, {
        type: "bar",
        label: "en-US",
        backgroundColor: "#beb391",
        data: [2, 3]
      }, {
        type: "line",
        label: "sv-SE",
        borderColor: "#b3cbaa",
        data: [1, 2],
        borderWidth: 2,
        fill: false,
        hideLegendItem: true // Hide it!
      }, {
        type: "bar",
        label: "sv-SE",
        backgroundColor: "#683e3a",
        data: [1, 2]
      }]
    },
    options: {
      hideLegendItemsPlugin : true, // Enable the plugin
      scales: {
        yAxes: [{
          ticks: {
            min: 0
          }
        }]
      }
    }
  };
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart-container"></canvas>


如果您想拥有图表计算标签的唯一性,您可以在幕后构建一组系列名称。

我还添加了一个选项来指定系列类型的优先级,即条形、线形等...如果您不指定优先级,则标签的最后出现将优先。这在您的示例中有效,因为条形系列位于线条系列之后。显然,这可以稍微调整一下。

var hideLegendItemsPlugin = {
  beforeInit: function(chartInstance) {
    let pluginOptions = chartInstance.options.hideLegendItemsPlugin;
    if (pluginOptions) {
      if (pluginOptions.priority) {
        const labelMap = chartInstance.data.datasets.reduce((ret, dataset) => {
          return Object.assign(ret, {
            [dataset.label] : (ret[dataset.label] || []).concat(dataset.type)
          });
        }, {});
        chartInstance.options.legend.labels.filter = (item, chart) => {
          let dataset = chart.datasets[item.datasetIndex];
          if (dataset.type !== pluginOptions.priority) {
            if (labelMap[dataset.label].includes(pluginOptions.priority)) {
              return false;
            }
          }
          return true;
        };
      } else {
        // Default prioritization is the last index (appearance) of that label.
        const labelMap = chartInstance.data.datasets.reduce((ret, dataset, index) => {
          return Object.assign(ret, { [dataset.label] : index });
        }, {});
        chartInstance.options.legend.labels.filter = (item, chart) => {
          let dataset = chart.datasets[item.datasetIndex];
          return item.datasetIndex === labelMap[dataset.label];
        };
      }
      chartInstance.options.legend.onClick = function(e, legendItem) {
        let chart = this.chart;
        let index = legendItem.datasetIndex;
        let visible = !chart.getDatasetMeta(index).hidden;
        chart.data.datasets.forEach((dataset, i) => {
          if (dataset.label === legendItem.text) {
            chart.getDatasetMeta(i).hidden = visible;
          }
        });
        chart.update();
      };
    }
  }
};
Chart.pluginService.register(hideLegendItemsPlugin);

const ctx = document.querySelector('#chart-container').getContext('2d');
new Chart(ctx, getConfig());

function getConfig() {
  return {
    type: 'bar',
    data: {
      labels: ["Mar-20", "Apr-20"],
      datasets: [{
        type: "line",
        label: "en-US",
        borderColor: "#8c856f",
        data: [2, 3],
        borderWidth: 2,
        fill: false
      }, {
        type: "bar",
        label: "en-US",
        backgroundColor: "#beb391",
        data: [2, 3]
      }, {
        type: "line",
        label: "sv-SE",
        borderColor: "#b3cbaa",
        data: [1, 2],
        borderWidth: 2,
        fill: false,
        hideLegendItem: true
      }, {
        type: "bar",
        label: "sv-SE",
        backgroundColor: "#683e3a",
        data: [1, 2]
      }]
    },
    options: {
      // This could also be simply `true` instead of an object.
      hideLegendItemsPlugin : {
        priority : 'bar' 
      },
      scales: {
        yAxes: [{
          ticks: {
            min: 0
          }
        }]
      }
    }
  };
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<canvas id="chart-container"></canvas>


推荐阅读