【问题标题】:How can I create a stacked bar chart with Charts.JS that displays relative rather than absolute values?如何使用 Charts.JS 创建显示相对值而不是绝对值的堆积条形图?
【发布时间】:2016-09-27 22:50:04
【问题描述】:

我正在使用 Charts.JS 创建一个看起来相当不错的堆积条形图:

不过,我需要的是让每个条形完全填满网格,绿色值是一个百分比,而不是绝对值,红色值以同样的方式为 100% - 不管是 %绿色值是。

IOW,对于上面屏幕截图中显示的值,我需要每个条形遍历整个宽度,并且以顶部条形为例,绿色部分上“标记”的文本应为“99%”

“99%”标签应取代“17,724”,“170”应完全消失。

这是当前使用的数据:

var priceComplianceData = {
    labels: [
        "Bix Produce", "Capitol City", "Charlies Portland", "Costa Fruit and Produce",
        "Get Fresh Sales",
        "Loffredo East", "Loffredo West", "Paragon", "Piazza Produce"
    ],
    datasets: [
    {
        label: "Price Compliant",
        backgroundColor: "rgba(34,139,34,0.5)",
        hoverBackgroundColor: "rgba(34,139,34,1)",
        data: [17724, 5565, 3806, 5925, 5721, 6635, 14080, 9027, 25553]
    },
    {
        label: "Non-Compliant",
        backgroundColor: "rgba(255, 0, 0, 0.5)",
        hoverBackgroundColor: "rgba(255, 0, 0, 1)",
        // The vals below have been multiplied by 10 (a 0 appended) so that the values are at least visible to the naked eye
        data: [170, 10, 180, 140, 30, 10, 50, 100, 10]
    }
    ]
}

...然后像这样添加到图表中:

var priceComplianceOptions = {
    scales: {
        xAxes: [
{
    stacked: true
}
        ],
        yAxes: [
{
    stacked: true
}
        ]
    },
    tooltips: {
        enabled: false
    }
};

var ctxBarChart = $("#priceComplianceBarChart").get(0).getContext("2d");
var priceBarChart = new Chart(ctxBarChart,
{
    type: 'horizontalBar',
    data: priceComplianceData,
    options: priceComplianceOptions
});

想到的是像这样更改数据:

datasets: [
{
    label: "Price Compliant",
    backgroundColor: "rgba(34,139,34,0.5)",
    hoverBackgroundColor: "rgba(34,139,34,1)",
    data: [99.0, 99.2, 99.4, 98.9, 99.1, 99.5, 99.6, 99.2, 99.7]
},
{
    label: "Non-Compliant",
    backgroundColor: "rgba(255, 0, 0, 0.5)",
    hoverBackgroundColor: "rgba(255, 0, 0, 1)",
    data: [1.0, 0.8, 0.6, 1.1, 0.9, 0.5, 0.4, 0.8, 0.3]
}
]

...然后使用第一个数据值作为标记绿色部分的值,并在其上附加一个“%”。

这是一个明智的做法吗?有没有更好的办法?

更新

建议的 beforeInit/afterDraw 不起作用;导致我或多或少可以接受的图表:

...比一个失控的消防站更紧张:

...即使我在两个函数的第一行都添加了这一行:

if (chartInstance.id !== 1) return; 

【问题讨论】:

    标签: javascript chart.js bar-chart chart.js2


    【解决方案1】:

    您的想法实际上非常好,但正如您在问题中声明的那样,它不会是动态的,因此不适用于这种类型的每个图表。

    使其动态化的一种方法是使用Chart.js plugins,其中将处理beforeInit 事件以编辑数据以适应您的百分比问题,并在其中处理afterDraw 事件以将百分比写入酒吧:

    Chart.pluginService.register({
    
        // This event helps to edit the data in our datasets to fit a percentage scale
        beforeInit: function(chartInstance) {
    
            // We first get the total of datasets for every data
            var totals = [];
            chartInstance.data.datasets.forEach(function(dataset) {
                for (var i = 0; i < dataset.data.length; i++) {
                    var total = 0;
                    chartInstance.data.datasets.forEach(function(dataset) {
                        total += dataset.data[i];
                    });
                    totals.push(total);
                }
            });
    
            // And use it to calculate the percentage of the current data
            chartInstance.data.datasets.forEach(function(dataset) {
                for (var i = 0; i < dataset.data.length; i++) {
                    // This ----------¬     is useful to add it as a string in the dataset
                    // It solves      V      the problem of values with `.0` at decimals
                    dataset.data[i] = '' + (dataset.data[i] / totals[i]) * 100;
                }
            });
        },
    
        // This event allows us to add the percentage on the bar
        afterDraw: function(chartInstance) {
    
            // We get the canvas context
            var ctx = chartInstance.chart.ctx;
    
            // And set the properties we need
            ctx.font = Chart.helpers.fontString(14, 'bold', Chart.defaults.global.defaultFontFamily);
            ctx.textAlign = 'center';
            ctx.textBaseline = 'bottom';
            ctx.fillStyle = '#666';
    
            // For every dataset ...
            chartInstance.data.datasets.forEach(function(dataset) {
    
                // If it is not the first dataset, we return now (no action)
                if (dataset._meta[0].controller.index != 0) return;
    
                // For ervery data in the dataset ...
                for (var i = 0; i < dataset.data.length; i++) {
    
                    // We get the model of the data
                    var model = dataset._meta[Object.keys(dataset._meta)[0]].data[i]._model;
    
                    // And use it to display the text where we want
                    ctx.fillText(parseFloat(dataset.data[i]).toFixed(2) + "%", ((model.base + model.x) / 2), (model.y + model.height / 3));
                }
            });
        }
    });
    


    您可以看到此代码在 on this jsFiddle 工作,这是它的结果:

    【讨论】:

    • 请看我的更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-15
    • 1970-01-01
    • 2021-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多