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();
    }
}

猜你喜欢

转载自blog.csdn.net/It_rod/article/details/80933220