Chart.js 堆叠柱状图点击更换背景色以及加虚线边框

这篇文章本应该昨天写出来的,只是昨天在本地demo测试的时候发现一个bug,当时差点要到Chart.js的github上添加issue了。

Demo需求:

  • 在柱状体click之后,当前堆叠柱状体更换背景颜色,以及添加虚线边框。然后点击其他的堆叠柱状图或者空白区域,原先的堆叠柱状图还要更新到原先的背景颜色。

解决方案:

  • 更换背景颜色需要将datasets内当前点击的index的所有bar进行背景替换。同时也要记忆原先的背景颜色,用于后面背景颜色的还原。
  • 虚线边框,由于Chart自身没有虚线边框的功能,所以只有自己去绘画出虚线边框。

第一版代码:

<!DOCTYPE html>  
<html lang="en-US">  
    <head>  
        <meta charset="UTF-8">  
        <title>Document</title>  
        <link rel="stylesheet/less" href="less/common.less" type="text/css">
        <script type="text/javascript" src="Chart.js" ></script>
    </head>  
    <body>  
        <canvas id="myChart" width="500px" height="400px"></canvas>
        <script>
        var ctx = document.getElementById("myChart").getContext('2d');

        Chart.defaults.derivedBubble = Chart.defaults.bar;

        // I think the recommend using Chart.controllers.bubble.extend({ extensions here });
        var custom = Chart.controllers.bar.extend({
            draw: function(ease) {
                // Call super method first
                Chart.controllers.bar.prototype.draw.call(this, ease);

                // Now we can do some custom drawing for this dataset. Here we'll draw a red box around the first point in each dataset
                var meta = this.getMeta();
                var pt0 = meta.data[0];

                //Only click the bar will draw the dash border
                if (lastClickBarArrayStyle.index != null) {
                    var pt0 = meta.data[lastClickBarArrayStyle.index];
                    var ctx = this.chart.chart.ctx;
                    ctx.save();

                    //The top bar need to draw the top border.
                    if (pt0._model.datasetLabel == this.chart.data.datasets[this.chart.data.datasets.length - 1].label) {
                        ctx.moveTo(pt0._view.x - pt0._view.width/2, pt0._view.y); 
                        ctx.lineTo(pt0._view.x + pt0._view.width/2, pt0._view.y); 
                    }

                    //All bar need to draw the right border.
                    ctx.moveTo(pt0._view.x + pt0._view.width/2, pt0._view.y); 
                    ctx.lineTo(pt0._view.x + pt0._view.width/2, pt0._view.base); 
    
                    //The bottom bar need to draw the bottom border.
                    if (pt0._model.datasetLabel == this.chart.data.datasets[0].label) {
                        ctx.moveTo(pt0._view.x + pt0._view.width/2, pt0._view.base); 
                        ctx.lineTo(pt0._view.x - pt0._view.width/2, pt0._view.base); 
                    }
    
                    //All bar need to draw the left border.
                    ctx.moveTo(pt0._view.x - pt0._view.width/2, pt0._view.base); 
                    ctx.lineTo(pt0._view.x - pt0._view.width/2, pt0._view.y); 
                    
                    ctx.setLineDash([5]);
                    ctx.lineWidth = 1;
                    ctx.strokeStyle = 'gray';
                    ctx.stroke();
                }
            }
        });

        // Stores the controller so that the chart initialization routine can look it up with
        // Chart.controllers[type]
        Chart.controllers.derivedBubble = custom;

        //Cache store the background color and the index.
        var lastClickBarArrayStyle = {
            index: null,
            style: [],
            reset: function() {
                this.index = null;
                this.style= [];
            }
        };

        var myChart = new Chart(ctx, {
            type: 'derivedBubble',
            data: {
                labels: ["Red", "Blue", "Yellow", "Green", "Purple", "Orange"],
                datasets: [{
                    label: '1# of Votes',
                    data: [12, 19, 3, 5, 2, 3],
                     stack: 'Stack 0',
                    backgroundColor: [
                        'rgba(255, 99, 132, 0.2)',
                        'rgba(255, 99, 132, 0.2)',
                        'rgba(255, 99, 132, 0.2)',
                        'rgba(255, 99, 132, 0.2)',
                        'rgba(255, 99, 132, 0.2)',
                        'rgba(255, 99, 132, 0.2)'
                    ],
                    borderColor: [
                        'rgba(255,99,132,1)',
                        'rgba(255,99,132,1)',
                        'rgba(255,99,132,1)',
                        'rgba(255,99,132,1)',
                        'rgba(255,99,132,1)',
                        'rgba(255,99,132,1)'
                    ],
                    borderWidth: 0
                },
                {
                    label: '2# of Votes',
                    data: [1, 2, 3, 4, 5, 6],
                     stack: 'Stack 0',
                    backgroundColor: [
                        'rgba(54, 162, 235, 0.2)',
                        'rgba(54, 162, 235, 0.2)',
                        'rgba(54, 162, 235, 0.2)',
                        'rgba(54, 162, 235, 0.2)',
                         'rgba(54, 162, 235, 0.2)',
                          'rgba(54, 162, 235, 0.2)'
                    ],
                    borderColor: [
                        'rgba(54, 162, 235, 1)',
                        'rgba(54, 162, 235, 1)',
                        'rgba(54, 162, 235, 1)',
                        'rgba(54, 162, 235, 1)',
                        'rgba(54, 162, 235, 1)',
                        'rgba(54, 162, 235, 1)'
                    ],
                    borderWidth: 0
                },
                {
                    label: '3# of Votes',
                    data: [10, 9, 8, 7, 6, 5],
                     stack: 'Stack 0',
                    backgroundColor: [
                        'rgba(200, 162, 235, 1)',
                        'rgba(200, 162, 235, 1)',
                        'rgba(200, 162, 235, 1)',
                        'rgba(200, 162, 235, 1)',
                         'rgba(200, 162, 235, 1)',
                          'rgba(200, 162, 235, 1)'
                    ],
                    borderColor: [
                        'rgba(200, 162, 235, 1)',
                        'rgba(200, 162, 235, 1)',
                        'rgba(200, 162, 235, 1)',
                        'rgba(200, 162, 235, 1)',
                        'rgba(200, 162, 235, 1)',
                        'rgba(200, 162, 235, 1)'
                    ],
                    borderWidth: 0
                }]
            },
            options: {
                scales: {
                    yAxes: [{
                        ticks: {
                            beginAtZero:true
                        },
                        stacked: true
                    }],
                    yAxes: [{
                            stacked: true
                    }]
                },
                onClick: function(event, bars) {
                    var points = this.getElementAtEvent(event);

                    if (points.length > 0) {
                        var index = points[0]._index;

                        //Restore last update bar backgroundcolor.
                        if (lastClickBarArrayStyle.index != null) {
                            var dataset = this.data.datasets;

                            for (i = 0; i < dataset.length; i++) {
                                dataset[i].backgroundColor[lastClickBarArrayStyle.index] = lastClickBarArrayStyle.style[i];
                            }

                            this.update();
                            lastClickBarArrayStyle.reset();
                        }
                        
                        //Update the bar to highlight backgroundcolor and cache the backgroundcolor
                        var dataset = this.data.datasets;

                        for (i = 0; i < dataset.length; i++) {
                            lastClickBarArrayStyle.index = index;
                            lastClickBarArrayStyle.style.push(dataset[i].backgroundColor[index]);
                            dataset[i].backgroundColor[index] = 'rgba(0,175,170, 0.3)';
                        }

                        this.update(this, {lazy:true});
                    } else {
                        var dataset = this.data.datasets;

                        for (i = 0; i < dataset.length; i++) {
                            dataset[i].backgroundColor[lastClickBarArrayStyle.index] = lastClickBarArrayStyle.style[i];
                        }

                        this.update();
                        lastClickBarArrayStyle.reset();
                    }
                }
            }
        });
        </script>
    </body>  
</html>   


如图:可以看到基本的需求已经实现了,但是可以看到最后的一个堆叠柱状图却会有边框,这不是我们想要的,这个效果存在的原因还不知道为什么,再加上自己确实chart.js和canvans不是很了解。所以昨天当时也是怎么测试都不行。最后想起来画虚线步骤,网上查看了一下,发现有一个步骤:

ctx.beginPath();

完整代码:

这里就不把所有的代码都贴出来了,只将draw的代码贴出来:

draw: function(ease) {
    // Call super method first
    Chart.controllers.bar.prototype.draw.call(this, ease);

    // Now we can do some custom drawing for this dataset. Here we'll draw a red box around the first point in each dataset
    var meta = this.getMeta();
    var pt0 = meta.data[0];

    //Only click the bar will draw the dash border
    if (lastClickBarArrayStyle.index != null) {
        var pt0 = meta.data[lastClickBarArrayStyle.index];

        var ctx = this.chart.chart.ctx;
        ctx.save();
        ctx.beginPath();
        
        //The top bar need to draw the top border.
        if (pt0._model.datasetLabel == this.chart.data.datasets[this.chart.data.datasets.length - 1].label) {
            ctx.moveTo(pt0._view.x - pt0._view.width/2, pt0._view.y); 
            ctx.lineTo(pt0._view.x + pt0._view.width/2, pt0._view.y); 
        }

        //All bar need to draw the right border.
        ctx.moveTo(pt0._view.x + pt0._view.width/2, pt0._view.y); 
        ctx.lineTo(pt0._view.x + pt0._view.width/2, pt0._view.base); 

        //The bottom bar need to draw the bottom border.
        if (pt0._model.datasetLabel == this.chart.data.datasets[0].label) {
            ctx.moveTo(pt0._view.x + pt0._view.width/2, pt0._view.base); 
            ctx.lineTo(pt0._view.x - pt0._view.width/2, pt0._view.base); 
        }

        //All bar need to draw the left border.
        ctx.moveTo(pt0._view.x - pt0._view.width/2, pt0._view.base); 
        ctx.lineTo(pt0._view.x - pt0._view.width/2, pt0._view.y); 
        
        ctx.setLineDash([5]);
        ctx.lineWidth = 1;
        ctx.strokeStyle = 'gray';
        ctx.stroke();
    }
}

Chart.js一个流行的JavaScript库,用于创建动态、交互式的图表,包括分组柱状图。分组柱状图,也叫堆积柱状图或多列柱状图,它将数据分为几个类别,并显示每个类别的累积值,通过逐级堆叠柱子来清晰地展示各个部分之间的比例关系。例如,在销售分析中,你可以按产品线对销售额进行分组并叠,以便一眼看出各个产品线的贡献。 在Chart.js中,创建分组柱状图通常涉及以下几个步骤: 1. 引入Chart.js库和适当的插件(如`chartjs-plugin-datalabels`用于数据标签)。 2. 定义数据对象,包含类别名和对应的数据数组。 3. 设置配置项,指定柱状图类型(`type: 'bar'`)、分组(`grouped: true`)和累积(`stacked: true`或`false`,默认为`true`表示堆积)。 4. 实例化一个新的Chart对象,并传入容器元素和配置数据。 以下是一个基本示例: ```javascript import { Bar, mixins } from 'chart.js'; import DataLabels from 'chartjs-plugin-datalabels'; // 创建新配置 const config = { type: 'bar', data: { labels: ['Category1', 'Category2', 'Category3'], datasets: [ { label: 'Group A', data: [10, 20, 30], backgroundColor: 'rgba(75, 192, 192, 0.6)', borderColor: 'rgba(75, 192, 192, 1)', }, { label: 'Group B', data: [40, 50, 60], backgroundColor: 'rgba(255, 99, 132, 0.6)', borderColor: 'rgba(255, 99, 132, 1)', } ], // 分组和累积设置 grouped: true, stacked: true, }, options: { plugins: { datalabels: { enabled: true, } }, }, }; // 绑定到canvas元素上 new Bar({ ...config, }, document.getElementById('myChart')); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值