如何强制我的 ChartJS 画布图例留在单个列中?

Posted

技术标签:

【中文标题】如何强制我的 ChartJS 画布图例留在单个列中?【英文标题】:How can I force my ChartJS canvas legend to stay in a single column? 【发布时间】:2019-06-09 15:52:06 【问题描述】:

我检查了一些不同的来源并查看了选项,但我似乎无法让我的图例留在一栏中。例如,

在上图中,您会注意到图例的一部分被切掉并放在一边。这发生在 https://jsfiddle.net/lochrine/02yrpcxg/

这里是相关的JS:

//Line Graph Script

$('.line-graph').each(function () 
    var legendlabels = $(this).data('legendlabels');
    var datapoints = $(this).data('datapoints');
    var suppliers = $(this).data('suppliers');

    var datatype = $(this).data('datatype');
    var yAxisString = "Amounts";
    if (datatype == "units")  yAxisString = "Units Sold"; 
    else if (datatype == "money")  yAxisString = "Amount (Dollars)"; 

    console.log(datatype);

    new Chart($(this).get(0).getContext('2d'), 
        type: 'line',
        data: 
            labels: legendlabels,
            datasets: $.map(datapoints, function (e, i) 
                return 
                    backgroundColor: lineChartColors[i],
                    borderColor: lineChartColors[i],
                    fill: false,
                    data: e,
                    label: suppliers[i],
                    lineTension: 0.2,
                
            )
        ,
        options: 
            layout: 
                padding: 
                    left: 20,
                    right: 40,
                    top: 20,
                    bottom: 20
                
            ,
            legend: 
                display: true,
                position: 'left'

            ,
            scales: 
                xAxes: [
                    scaleLabel: 
                        display: true,
                        labelString: 'Month'
                    
                ],
                yAxes: [
                    ticks: 
                        beginAtZero: true,
                        callback: function (value, index, values) 
                            return addCommas(value);
                        
                    ,
                    scaleLabel: 
                        display: true,
                        labelString: yAxisString 
                    
                ]
            ,
            plugins: 
                datalabels: 
                    display: false
                
            ,
            tooltips: 
            callbacks: 
                label: function (tooltipItem, data) 
                    var datasetLabel = data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index].toString();
                    var label = data.datasets[tooltipItem.datasetIndex].label + ': ';
                    var formattedReturnLabel;
                    if (datatype == "money") 
                        formattedReturnLabel = label + '$' + addCommas(datasetLabel);
                     else 
                        formattedReturnLabel = label + addCommas(datasetLabel);
                    
                    return formattedReturnLabel;
                
            
        
        
    );

)

以及相关的html

<div class="widget widget-double">
    <div class="m-3 border">
        <table style="cursor: pointer !important;" onclick="window.location.href='@Url.Action("SupplierUnitsByMonth", "Reports")'" class="table mb-0"><thead><tr><th class="text-center">@ViewBag.widgetName</th></tr></thead></table>
        <div class="w-100 aspect-ratio-50 p-2">
            <canvas id="chart-units-history" data-legendlabels="[@ViewBag.Months]" data-suppliers= "[@ViewBag.suppliers]" data-datapoints="[@ViewBag.supplierTotals]" data-datatype="units" class="line-graph w-100 aspect-ratio-50"></canvas>
        </div>
    </div>
</div>

【问题讨论】:

【参考方案1】:

您可以使用legendCallback 和一些CSS 生成自定义HTML legend。

legendCallback: chart => 
  let html = '<ul>';
  chart.data.datasets.forEach((ds, i) => 
    html += '<li>' +
      '<span style="width: 36px; height: 14px; background-color:' + ds.backgroundColor + '; border:' + ds.borderWidth + 'px solid ' + ds.borderColor + '" onclick="onLegendClicked(event, \'' + i + '\')">&nbsp;</span>' +
      '<span id="legend-label-' + i + '" onclick="onLegendClicked(event, \'' + i + '\')">' +
      ds.label + '</span>' +
      '</li>';
  );
  return html + '</ul>';
,

为了使其行为与标准 Chart.js 图表相同,当鼠标单击图例标签时会调用函数 onLegendClicked。此功能切换单个数据集的隐藏状态,并在正常和删除线之间更改标签文本样式。

function onLegendClicked(e, i) 
  const hidden = !chart.data.datasets[i].hidden;
  chart.data.datasets[i].hidden = hidden;
  const legendLabelSpan = document.getElementById("legend-label-" + i);
  legendLabelSpan.style.textDecoration = hidden ? 'line-through' : '';
  chart.update();
;

请查看您修改后的代码,看看它是如何工作的。

const lineChartColors = ["#000000", "#fd7730", "#ffd35c", "#3fc6f3", "#28a745", "#488cf2", "#4755d3", "#9768c9", "#f2748d", "#f287e7", '#992499', '#6BD69E'];
const legendlabels = ['Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'];
const datapoints = [
  [205, 275, 359, 329, 262, 302, 290, 323, 279, 238, 307, 245],
  [16, 13, 14, 11, 23, 11, 24, 23, 15, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 0],
  [169, 194, 261, 193, 151, 158, 128, 143, 163, 173, 139, 208],
  [8, 5, 8, 2, 4, 4, 0, 0, 0, 0, 0, 0],
  [0, 0, 19, 36, 7, 35, 27, 30, 13, 0, 0, 0],
  [0, 47, 30, 54, 59, 48, 41, 38, 65, 24, 44, 37],
  [12, 16, 27, 33, 18, 46, 70, 89, 23, 41, 71, 0]
];
var suppliers = ["Total", "Starkey", "Resound", "Widex", "Rexton", "Unitron", "Phonak", "Signia"];

function onLegendClicked(e, i) 
  const hidden = !chart.data.datasets[i].hidden;
  chart.data.datasets[i].hidden = hidden;
  const legendLabelSpan = document.getElementById("legend-label-" + i);
  legendLabelSpan.style.textDecoration = hidden ? 'line-through' : '';
  chart.update();
;

const chart = new Chart('myChart', 
  type: 'line',
  data: 
    labels: legendlabels,
    datasets: datapoints.map((e, i) => (
      backgroundColor: lineChartColors[i],
      borderColor: lineChartColors[i],
      fill: false,
      data: e,
      label: suppliers[i],
      lineTension: 0.2,
    ))
  ,
  options: 
    legend: 
      display: false
    ,
    legendCallback: chart => 
      let html = '<ul>';
      chart.data.datasets.forEach((ds, i) => 
        html += '<li>' +
          '<span style="width: 36px; height: 14px; background-color:' + ds.backgroundColor + '; border:' + ds.borderWidth + 'px solid ' + ds.borderColor + '" onclick="onLegendClicked(event, \'' + i + '\')">&nbsp;</span>' +
          '<span id="legend-label-' + i + '" onclick="onLegendClicked(event, \'' + i + '\')">' +
          ds.label + '</span>' +
          '</li>';
      );
      return html + '</ul>';
    ,
    scales: 
      xAxes: [
        scaleLabel: 
          display: true,
          labelString: 'Month'
        
      ],
      yAxes: [
        ticks: 
          beginAtZero: true
        ,
        scaleLabel: 
          display: true,
          labelString: "Units Sold"
        
      ]
    ,
    plugins: 
      datalabels: 
        display: false
      
    
  
);
document.getElementById("legend").innerHTML = chart.generateLegend();
#chart-wrapper 
  display: flex;


ul 
  list-style-type: none;
  margin: 0;
  padding: 0;


#legend li 
  cursor: pointer;
  display: flex;
  padding: 0 10px 5px 0;


#legend li span 
  padding-left: 8px;
  font-family: Arial, Helvetica, sans-serif;
  font-size: 12px;
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<div id="chart-wrapper">
  <div id="legend"></div>
  <canvas id="myChart" ></canvas>
</div>

【讨论】:

以上是关于如何强制我的 ChartJS 画布图例留在单个列中?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Chartjs 对图例条目进行分组?

有没有办法隐藏/自定义 ChartJS 上面的图例?

如何通过画布外的按钮(ChartJs)控制画布图像的可见性?

如何在单个查询 (PSQL) 中更新一列中的不同值?

如何从 react-chartjs-2 的画布中删除一条垂直线?

Chart js 更新多个图表