Chart.js 堆叠条形图 - 按值对条形图中的值进行排序

问题描述 投票:0回答:2

我尝试使用 Chart.js 框架(版本 2.7)实现堆叠条形图,但遇到了以下问题:每个条形图中单个数据元素的绘制顺序与 JSON 结构中提供的顺序相同(这绝对符合逻辑且易于理解)。我希望对它们进行排序:最大的元素位于栏的底部,最小的位于顶部。

我想它可以使用一些插件定制黑魔法来完成,但无法弄清楚。

如有任何帮助,我们将不胜感激。

示例:

var chart = new Chart(document.getElementById('canvas'), {
				type : 'bar',
				data : {
					labels : ["2018-07-06", "2018-07-07", "2018-07-08", "2018-07-09", "2018-07-10"],
					datasets : [
						{
							label: "Dataset 1", 
							backgroundColor: "red", 
							data: [ {x: "2018-07-06", y: 1}, {x: "2018-07-07", y: 2}, {x: "2018-07-08", y: 3}]
						}, 
						{
							label: "Dataset 2", 
							backgroundColor: "blue", 
							data: [ {x: "2018-07-06", y: 3}, {x: "2018-07-07", y: 2}, {x: "2018-07-08", y: 1}]
						}, 
						{
							label: "Dataset 3", 
							backgroundColor: "green", 
							data: [ {x: "2018-07-06", y: 2}, {x: "2018-07-07", y: 1}, {x: "2018-07-08", y: 2}]
						}
					],
					borderWidth : 1
				},
				options : {
					responsive : true,
					maintainAspectRatio : false,
					title : {
						display : true,
						text : 'Test'
					},
					scales : {
						xAxes : [ {
							stacked : true,
							time : {
								unit : 'day'
							}
						} ],
						yAxes : [ {
							stacked : true,
							ticks : {
								beginAtZero : true
							}
						} ]
					}
				}
			});
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
<canvas id="canvas" height="500" width="500"></canvas>

在此示例中,我希望每列中的颜色顺序颠倒: 第一个是蓝-绿-红,
第二个是红-蓝-绿或蓝-红-绿, 第三个是红-绿-蓝。

charts chart.js bar-chart chart.js2
2个回答
3
投票

也许可以以某种方式更高效和/或更美观,但我必须自己解决它。

/*
* chart.js do not support stacked bar charts, which are sorted by value,
* therefore it's needed to perform this functionality with custom plugins
*/
Chart.plugins.register({

/*
* Sorts data by value and calculates values for bar stacks
*/
beforeDraw(chart) {

    // create data container in chart instance
    chart.sortedData = {};
    
    // iterate over datasets
    chart.data.datasets.forEach((dataset, datasetIndex) => {
    
        // iterate over dataset records
        dataset.data.forEach((data, index) => {
        
            // create data container for bar stack data
            if(!chart.sortedData[index]) {
                chart.sortedData[index] = {
                    data: []
                };
            }
            
            // save data
            chart.sortedData[index].data[datasetIndex] = {
                datasetIndex: datasetIndex,
                hidden: chart.getDatasetMeta(datasetIndex).hidden ? true : false,
                color: dataset.backgroundColor,
                value: dataset.data[index].y,
                y: chart.getDatasetMeta(datasetIndex).data[index]._model.y,
                base: chart.getDatasetMeta(datasetIndex).data[index]._model.base,
            };
            
        });
    });
    
    var chartTop = chart.scales['y-axis-0'].top;
    var max = chart.scales['y-axis-0'].max;
    var h = chart.scales['y-axis-0'].height / max;
    
    // iterate over datasets
    chart.data.datasets.forEach((dataset, datasetIndex) => {
        
        // iterate over dataset records
        dataset.data.forEach((data, index) => {
        
            // sort data in bar stack by value
            chart.sortedData[index].data = Object.keys(chart.sortedData[index].data)
                .map(k => chart.sortedData[index].data[k])
                .sort((a, b) => a.value - b.value);
                
            // iterate over stack records
            chart.sortedData[index].data.forEach((d, i) => {
            
                // calculate base value
                d.base = chartTop + (max - Object.keys(chart.sortedData[index].data)
                    .map(k => chart.sortedData[index].data[k].value)
                    .reduce((a, b) => a + b, 0)) * h
                    + Object.keys(chart.sortedData[index].data)
                    .map(k => chart.sortedData[index].data[k])
                    .filter(d => d.hidden)
                    .reduce((a, b) => a + b.value, 0) * h;                  
                
                // increase base value with values of previous records
                for (var j = 0; j < i; j++) {
                    d.base += chart.sortedData[index].data[j].hidden 
                        ? 0 
                        : h * chart.sortedData[index].data[j].value;
                }
                
                // set y value
                d.y = d.base + h * d.value;
                
            });
        });
    });
},

/*
* Sets values for base and y
*/
beforeDatasetDraw(chart, args) {
    chart.getDatasetMeta(args.index).data.forEach((data, index) => {
        var el = chart.sortedData[index].data.filter(e => e.datasetIndex === args.index)[0];
        data._model.y = el.y;
        data._model.base = el.base;
    });
   }
   
});

var chart = new Chart(document.getElementById('canvas'), {
                type : 'bar',
                data : {
                    labels : ["2018-07-06", "2018-07-07", "2018-07-08", "2018-07-09", "2018-07-10"],
                    datasets : [
                        {
                            label: "Dataset 1", 
                            backgroundColor: "red", 
                            data: [ {x: "2018-07-06", y: 1}, {x: "2018-07-07", y: 2}, {x: "2018-07-08", y: 3}]
                        }, 
                        {
                            label: "Dataset 2", 
                            backgroundColor: "blue", 
                            data: [ {x: "2018-07-06", y: 3}, {x: "2018-07-07", y: 2}, {x: "2018-07-08", y: 1}]
                        }, 
                        {
                            label: "Dataset 3", 
                            backgroundColor: "green", 
                            data: [ {x: "2018-07-06", y: 2}, {x: "2018-07-07", y: 1}, {x: "2018-07-08", y: 2}]
                        }
                    ],
                    borderWidth : 1
                },
                options : {
                    responsive : true,
                    maintainAspectRatio : false,
                    title : {
                        display : true,
                        text : 'Test'
                    },
                    scales : {
                        xAxes : [ {
                            stacked : true,
                            time : {
                                unit : 'day'
                            }
                        } ],
                        yAxes : [ {
                            stacked : true,
                            ticks : {
                                beginAtZero : true
                            }
                        } ]
                    }
                }
            });
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.bundle.min.js"></script>
<canvas id="canvas" height="500" width="500"></canvas>


0
投票

感谢您的工作,但遗憾的是它不适用于最新版本的 Chart.js (4.4.1)。这是我的返工作品。

注意:我添加了 options.plugins.SortedStack.enabled,它可以根据需要启用或禁用插件。

var Sorted = {
        id: 'SortedStack',
        beforeDraw: function (chart, args, options) {
            if (options.enabled) {
                options.sortedData = [];

                for (var datasetIndex = 0; datasetIndex < chart.data.datasets.length; datasetIndex++) {
                    var dataset = chart.data.datasets[datasetIndex];


                    for (var index = 0; index < dataset.data.length; index++) {
                        var data = dataset.data[index];

                        if (!options.sortedData[index]) {
                            options.sortedData[index] = {
                                data: []
                            };
                        }

                        options.sortedData[index].data[datasetIndex] = {
                            datasetIndex: datasetIndex,
                            hidden: chart.getDatasetMeta(datasetIndex).hidden ? true : false,                            
                            value: dataset.data[index],
                            y: chart.getDatasetMeta(datasetIndex).data[index].y,
                            base: chart.getDatasetMeta(datasetIndex).data[index].base,
                        };

                    };
                };
                
                var chartTop = chart.scales.y.top;
                var max = chart.scales.y.max;
                var h = chart.scales.y.height / max;

                for (var datasetIndex = 0; datasetIndex < chart.data.datasets.length; datasetIndex++) {
                    var dataset = chart.data.datasets[datasetIndex];
                    

                    for (var index = 0; index < dataset.data.length; index++) {
                        var data = dataset.data[index];
                     
                        options.sortedData[index].data.sort(function (a, b) { return a.value - b.value; });
                        
                        for (var i = 0; i < options.sortedData[index].data.length; i++) {
                            var d = options.sortedData[index].data[i];
                
                            d.base = chartTop + (max - options.sortedData[index].data.reduce((a, b) => a + b.value, 0)) * h
                                + options.sortedData[index].data.filter(d => d.hidden).reduce((a, b) => a + b.value, 0) * h;
      
                            for (var j = 0; j < i; j++) {
                                d.base += options.sortedData[index].data[j].hidden
                                    ? 0
                                    : h * options.sortedData[index].data[j].value;
                            }

                            d.y = d.base + h * d.value;

                        };
                    }
                }
            }
        },
        beforeDatasetDraw: function (chart, args, options) {
            if (options.enabled) {
                chart.getDatasetMeta(args.index).data.forEach((data, index) => {
                    var el = options.sortedData[index].data.filter(e => e.datasetIndex === args.index)[0];
                    data.y = el.y;
                    data.base = el.base;
                });
            }
        }
    };
    
 var config =   {
  data: {
    labels: [
              "2018-07-06", "2018-07-07", "2018-07-08"       
    ],
    datasets: [
      {
        label: [
          "Dataset 1"
        ],
        data: [          1,2,3        ],
        backgroundColor: "red"
      },
      {
        label: [
          "Dataset 2"
        ],
        data: [          3,2,1        ],
        backgroundColor: "blue"
      },
      {
        label: [
          "Dataset 3"
        ],
        data: [          2,1,2        ],
        backgroundColor: "green"
      }
    ]
  },
  type: "bar",
  options: {
    responsive : true,
    maintainAspectRatio: false,
    plugins: {      
      SortedStack: {
        enabled: true
      }
    },    
    scales: {
      y: {
        axis: "y",
        type: "linear",
        Id: "y",
        stacked: true,
        position: "left",
        beginAtZero: true,
        ticks: {
          precision: 0
        }
      },
      x: {
        axis: "x",
        type: "category",
        Id: "x",
        stacked: true,
        position: "bottom",
        beginAtZero: false
      }
    },
    plugins: [Sorted]
  }
};

var chart = new Chart(document.getElementById('canvas'),config);
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
<canvas id="canvas" height="500" width="500"></canvas>

© www.soinside.com 2019 - 2024. All rights reserved.