如何在Chart.js中将正值置于堆叠条形图的顶部,负值置于底部

3
formatter函数给出了每组堆叠柱状图的总数,我正在尝试通过将datalabels向上定位(如果总数为正)或向下定位(如果总数为负)来完成。如果能得到任何帮助,我将非常感激。

enter image description here

datalabels: {
    anchor: (context) =>{
        const anchor = [];
        let sum = ?;
        
        if(parseFloat(sum) >=0){
            anchor.push('end');
        }else{
            anchor.push('start');
        }
        return anchor;
    },
    align: (context) =>{
        const align = [];
        let sum = ?;

        if(parseFloat(sum) >=0){
            align.push('end');
        }else{
            align.push('bottom');
        }
        return align;

    },
    formatter:(value, context) =>{
        const datasetArray = [];
        context.chart.data.datasets.forEach((dataset) =>{
            if(dataset.data[context.dataIndex] != undefined){
                datasetArray.push(dataset.data[context.dataIndex]);
            }
            
        });
        
        function totalSum(total, datapoint){                                           
            return +total + +datapoint;
        }
        
        let sum = datasetArray.reduce(totalSum);
        if(context.datasetIndex === datasetArray.length - 1){
            return parseFloat(sum).toFixed(2) ;
        }else{
            return '';
        }
    },
}

请你能否在CodeSandbox或类似的工具中创建一个最小可复现的示例? - AndreFeijo
请问您能否在CodeSandbox或类似的工具中创建一个最小可复现的示例? - AndreFeijo
2个回答

2
目前还不清楚这些条形图是如何堆叠的,有许多未知的选项。根据图表的视觉外观,可能的组合如下:

const N = 10;
const dataGenerator = (plus = 600, minus = 400, pNull = 1) =>
    Array.from({length: N}, ()=>Math.random() < pNull ?
        Math.round(Math.random()*(plus+minus)-minus)/4 : null)
const ctx1 = document.getElementById('chart1');
new Chart(ctx1, {
    type: "bar",
    plugins: [ChartDataLabels],
    data: {
        labels: Array.from({length: N}, (_, i)=>'l'+(i+1)),
        datasets: [
            {
                data: dataGenerator(),
                stack: 'a',
            },
            {
                data: dataGenerator() ,
                stack: 'a',
            },
            {
                data: dataGenerator(100, 50, 0.5),
                stack: 'a',
            },
        ]
    },
    options: {
        indexAxis: 'x',
        layout: {
            padding: {
                top: 20,
                bottom: 20
            }
        },
        animation: {
            duration: 0
        },
        scales: {
            x: {
                ticks:{
                    display: false
                }
            },
            y: {
                stacked: true,
                beginAtZero: true
            }
        },
        plugins:{
            legend:{
                display: false
            },
            datalabels:{
                formatter: (value, context) => {
                    const {dataIndex, datasetIndex, chart} = context;
                    const dataForDataIndex = chart.data.datasets.map(
                        dataset=>dataset.data[dataIndex] ?? 0
                    );
                    const total = dataForDataIndex.reduce((s, x)=>s+x)
                    // the index of the dataset that contains the last point (at dataIndex)
                    // with the same sign as the total - that is the one that should carry
                    // the total label
                    const datasetIndexLast = dataForDataIndex.findLastIndex(x => x * total > 0);
                    context.total = total;
                    return datasetIndexLast === datasetIndex ? total.toFixed(2) : null
                },
                anchor: (context) => {
                    return context.total > 0 ? 'end' : 'start'
                },
                align: (context) => {
                    return context.total > 0 ? 'top' : 'bottom';
                },

                clip: false
            }
        }
    }
});
<div style="height:500px">
    <canvas id="chart1"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.3.0/chart.umd.js"
        integrity="sha512-CMF3tQtjOoOJoOKlsS7/2loJlkyctwzSoDK/S40iAB+MqWSaf50uObGQSk5Ny/gfRhRCjNLvoxuCvdnERU4WGg=="
        crossOrigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/chartjs-plugin-datalabels/2.2.0/chartjs-plugin-datalabels.min.js" integrity="sha512-JPcRR8yFa8mmCsfrw4TNte1ZvF1e3+1SdGMslZvmrzDYxS69J7J49vkFL8u6u8PlPJK+H3voElBtUCzaXj+6ig==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

这个想法是为了使数据标签对数据集中的所有柱形图启用,但选择正确位置来表示来自formatter的总和 - 即当前dataIndex的值与总和具有相同符号的最后一个数据集。如果使用了order选项,情况可能会有所不同。
代码还利用了一个事实,即在相同的datasetIndex下,context对象是共享的,它在formatteralignanchor方法之间共享。其中formatter始终是第一个被调用的,如果formatter返回null,其他方法甚至不会被调用。因此,它将当前datasetIndex的总和保存在context对象中,以便只有当alignanchor被调用时,才使用该总和来计算唯一的一个点(每个datasetIndex)。为了更安全的解决方案,不使用这个未记录的事实,可以在alignanchor中重新计算总和,或者使用外部对象缓存总和,这样可以通过仅计算一次总和来优化计算,而只在第一次为数据集调用formatter时进行。

1
非常感谢。这正是我正在寻找的东西。 - Abzal Ali
1
非常感谢。这正是我在寻找的。 - Abzal Ali
1
非常感谢你。这正是我正在寻找的。 - undefined

0
你可以像这样使用chartjs-plugin-datalabels,请参考这里
Chart.defaults.set('plugins.datalabels', {
  color: '#FE777B'
});

var chart = new Chart(ctx, {
  options: {
    plugins: {
      // Change options for ALL labels of THIS CHART
      datalabels: {
        color: '#36A2EB',
        anchor: ()=>/*your condition here*/
      }
    }
  },
  data: {
    datasets: [{
      // Change options only for labels of THIS DATASET
      datalabels: {
        color: '#FFCE56'
      }
    }]
  }
});

此外,还要检查定位属性。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接