首页 > 解决方案 > Chart.js 自定义,两个 Y 轴叠加,图表区域填充,奇数刻度填充

问题描述

Chart.js 插件超级、优秀、美观且易于定制。但即使这一切也没有帮助我解决一些问题。

我必须根据图 1 所示的设计创建像素完美图表

我希望能得到你的帮助!!

  1. 如何使那些没有签名的行那些有签名的行长
  2. 我使用tickMarkLength参数从刻度到图形的缩进,但也许它可能以某种方式在另一个之下,因为您可以看到一个刻度在另一个刻度上的重叠。
  3. 如何使左右刻度的网格线重合?我设置了 beforeUpdate .stepSize,但尽管我指定应该有 8 个间隔,有时是 8,然后是 9。

这是我必须创建的图表的设计

我当前的代码有一个链接:

function data_generation(values_obj) {
        let max_val=-900;
        let min_val=0;
    Object.keys(values_obj).forEach(function(key) {
        chart_object={};
        chart_object.label= values_obj[key].name;
        chart_object.data= Object.values(values_obj[key].data);
        chart_object.backgroundColor= values_obj[key].color;
        if(key == 'TempOutdoor') {
            chart_object.yAxisID = 'right-y-axis';
            chart_object.backgroundColor= "transparent";
            chart_object.pointRadius= 4;
            chart_object.lineTension= 0;
            chart_object.pointBackgroundColor="#FFF";
            chart_object.pointBorderColor= "#60AD5E";
            chart_object.borderColor= "#60AD5E";
            chart_object.pointBorderWidth= 2;
            chart_object.type= 'line';
        } else {
            chart_object.yAxisID = 'left-y-axis';
            chart_object.lineTension= 0;
        }       
        config.data.datasets.push(chart_object);
        //find common min and max values        
        //min
        if(min_val>parseFloat(values_obj[key].min)) {
            min_val = parseFloat(values_obj[key].min);
        }
        //max
        if(max_val < parseFloat(values_obj[key].max)) {
            max_val = parseFloat(values_obj[key].max);
        }
    });   
}
var config = {
    drawTicks:false,
    type: 'bar',
    data: {
        datasets: [   ],
        labels: ''
    },
    options:  {
        animation: {
            duration: 0
        },
        'legend':false,            
        responsive:true,
        maintainAspectRatio: false,
        scales: {
            xAxes: [{
                stacked: true,
                barThickness: ($(window).width()<991.99)?14:24,
                ticks: {
                    fontSize: ($(window).width()<991.99)?10:14,
                },
                gridLines : {
                    display : false
                }
            }],
            yAxes: [
                {
                    beforeUpdate: function(scale) {
                        //1 find max and min through all leftlabels
                    var left_side_list = config.data.datasets.filter(obj => {return obj.yAxisID == "left-y-axis"});
                    var left_side_list_data = [].concat(...Object.keys(left_side_list).map(e => left_side_list[e].data));                        
                        let max_val = Math.max.apply(Math,left_side_list_data);
                        let min_val = Math.min.apply(Math,left_side_list_data);
                        // 8  intervals - 9 lines
                        let left_iterval = (max_val - min_val) / 8;
                        //set stepsize                            
                        scale.chart.options.scales.yAxes[0].ticks.stepSize = left_iterval;
                        return;
                    },
                    id: 'left-y-axis',
                    type: 'linear',
                    position: 'left',
                    ticks: {
                        beginAtZero: false,
                        fontSize: ($(window).width()<991.99)?10:14,
                        callback: function(value, index, values) {           

                                if(index % 2 == 0 || index==0) {
                                    return '   ';
                                } else {
                                    return " "+value.toFixed(0)+" ";
                                }
                        }
                    },
                    gridLines: {
                        drawBorder: false,
                        tickMarkLength:  ($(window).width()<991.99)?34:84,                            
                    }
                },
                {
                    beforeUpdate: function(scale) {
                        //var nMaxRev = Math.max.apply(Math,scale.chart.config.data.datasets[1].data);
                        //get right object data
                        var temp_list = config.data.datasets.filter(obj => {return obj.yAxisID == "right-y-axis"});
                        //var temp_list = scale.chart.config.data.datasets.filter(obj => {return obj.yAxisID == "right-y-axis"});
                        //console.log(temp_list);
                        if(temp_list[0].data !== undefined || temp_list[0].data != []) {
                            var nMaxRev = Math.max.apply(Math, temp_list[0].data);
                            var nMinRev = Math.min.apply(Math, temp_list[0].data);
                            var nLeftTickCount = 8;
                            if(nMinRev<0) {
                                nLeftTickCount = 7;
                            }
                            var nTickInterval = (nMaxRev - nMinRev) / nLeftTickCount;
                            scale.chart.options.scales.yAxes[1].ticks.stepSize = nTickInterval;
                        }
                        return;
                    },
                    id: 'right-y-axis',
                    type: 'linear',
                    position: 'right',
                    ticks: {
                        beginAtZero: false,
                        fontSize: ($(window).width()<991.99)?10:14,
                        callback: function(value, index, values) {
                            if(index % 2 == 0 || index==0) {
                                return '';
                            } else {
                                return "   "+value.toFixed(0);
                            }
                        }
                    },
                    gridLines: {
                        drawBorder: false,
                        tickMarkLength:  ($(window).width()<991.99)?34:84,                       
                    }
                }
            ]
        }
    }
};


window.onload = function() {

var data_string='{"success":true,"axis":["Пн","Вт","Ср","Чт","Пт","Сб","Вс"],"data":{"TempOutdoor":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":-4.9787234042553195,"Вт":-2.9166666666666665,"Ср":-3.3125,"Чт":2.5208333333333335,"Пт":6.84375,"Сб":0,"Вс":0},"min":"-4.98","max":"6.84","avg":"-0.26","sum":"-1.84","name":"Температура на улице","color":"#60AD5E","value_type":"instant"},"MotoHW":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":11,"Вт":15,"Ср":13,"Чт":12,"Пт":9,"Сб":0,"Вс":0},"min":"0.00","max":"15.00","avg":"8.57","sum":"60.00","name":"Мотогодини: Гаряча вода","color":"#29819D","value_type":"counter"}},"closestPeriods":{"previous":{"2021-05-03 00:00:00":"03.05 - 09.05"},"current":{"2021-05-10 00:00:00":"10.05 - 16.05"},"next":null}}';
  var data = JSON.parse(data_string);    

                config.data.labels = data.axis;
                var values_obj = data.data;
                data_generation(values_obj);
                //console.log(JSON.stringify(config));
                var ctx = document.getElementById('StatisticsChartCanvas').getContext('2d');
                window.StatisticsChart = new Chart(ctx,config); 

};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="chart-wrapper" style="width:548px; height:265px;">
  <canvas id="StatisticsChartCanvas"></canvas>
</div>

标签: chart.jsaxis

解决方案


经过大量的想法、尝试和错误,我找到了完全定制秤的解决方案。

为此我

  1. 使用属性禁用网格线和刻度的显示
    display:false,
  1. 编写了一个插件,可以根据设计绘制网格线和刻度线。这就是发生的事情

var AikGridLinePlugin = {
    beforeDraw: function(chartInstance) {
        var yScaleLeft = chartInstance.scales["left-y-axis"];
        var yScaleRight = chartInstance.scales["right-y-axis"];
        var canvas = chartInstance.chart;
        var ctx = canvas.ctx;

        //left axis
        var left_side_list = chartInstance.data.datasets.filter(obj => {return obj.yAxisID == "left-y-axis"});
        var left_side_list_data = [].concat(...Object.keys(left_side_list).map(e => left_side_list[e].data));
        let left_side_list_max = Math.max.apply(Math,left_side_list_data);
        let left_side_list_min = Math.min.apply(Math,left_side_list_data);
        let left_iterval = (left_side_list_max - left_side_list_min) / 8;

        // right axis
        var right_side_list = chartInstance.data.datasets.filter(obj => {return obj.yAxisID == "right-y-axis"});
        var right_side_list_data = [].concat(...Object.keys(right_side_list).map(e => right_side_list[e].data));
        let right_side_list_max = Math.max.apply(Math,right_side_list_data);
        let right_side_list_min = Math.min.apply(Math,right_side_list_data);
        let right_iterval = (right_side_list_max - right_side_list_min) / 8;

        var current_value_left = left_side_list_min,
            current_value_right = right_side_list_min,
            current_value_right_text=0;
        for(var i=1;i<10;i++) {
            ctx.lineWidth = 1;
            ctx.font = "13px Roboto";
            ctx.fillStyle = "#666666";
            ctx.beginPath();
            if(i%2==0) {
                ctx.moveTo(47, yScaleLeft.getPixelForValue(current_value_left));
                ctx.lineTo((canvas.width-47), yScaleLeft.getPixelForValue(current_value_left));
                ctx.fillText((current_value_left>1)?current_value_left.toFixed(0):current_value_left.toFixed(1), 5, yScaleLeft.getPixelForValue(current_value_left)+5);
                current_value_right_text=(current_value_right>1 || current_value_right<-1)?current_value_right.toFixed(0):current_value_right.toFixed(1);
                ctx.fillText(current_value_right_text, (canvas.width-5-ctx.measureText(current_value_right_text).width), yScaleLeft.getPixelForValue(current_value_left)+5);
            } else {
                ctx.moveTo(15, yScaleLeft.getPixelForValue(current_value_left));
                ctx.lineTo((canvas.width-15), yScaleLeft.getPixelForValue(current_value_left));
            }
            ctx.strokeStyle = "#91979F";
            ctx.stroke();
            current_value_left = current_value_left+left_iterval;
            current_value_right = current_value_right+right_iterval;
        }
        return;


    }
};
Chart.pluginService.register(AikGridLinePlugin);

function data_generation(values_obj) {
        let max_val=-900;
        let min_val=0;
    Object.keys(values_obj).forEach(function(key) {
        chart_object={};
        chart_object.label= values_obj[key].name;
        chart_object.data= Object.values(values_obj[key].data);
        chart_object.backgroundColor= values_obj[key].color;
        if(key == 'TempOutdoor') {
            chart_object.yAxisID = 'right-y-axis';
            chart_object.backgroundColor= "transparent";
            chart_object.pointRadius= 4;
            chart_object.lineTension= 0;
            chart_object.pointBackgroundColor="#FFF";
            chart_object.pointBorderColor= "#60AD5E";
            chart_object.borderColor= "#60AD5E";
            chart_object.pointBorderWidth= 2;
            chart_object.type= 'line';
        } else {
            chart_object.yAxisID = 'left-y-axis';
            chart_object.lineTension= 0;
        }       
        config.data.datasets.push(chart_object);
        //find common min and max values        
        //min
        if(min_val>parseFloat(values_obj[key].min)) {
            min_val = parseFloat(values_obj[key].min);
        }
        //max
        if(max_val < parseFloat(values_obj[key].max)) {
            max_val = parseFloat(values_obj[key].max);
        }
    });   
}
var config = {
    drawTicks:false,
    type: 'bar',
    data: {
        datasets: [   ],
        labels: ''
    },

    options:  {

        animation: {
            duration: 0
        },
        'legend':false,
        responsive:true,
        maintainAspectRatio: false,
        scales: {
            xAxes: [{
                id: 'x-axis',
                stacked: true,
                barThickness: ($(window).width()<991.99)?14:24,
                ticks: {
                    fontSize: ($(window).width()<991.99)?10:14,

                },
                gridLines : {
                    display : false
                }
            }],
            yAxes: [
                {
                    id: 'left-y-axis',
                    type: 'linear',
                    position: 'left',
                    ticks: {
                        display:false,
                    },
                    gridLines: {
                        display : false,
                        drawBorder: false,
                        tickMarkLength:  ($(window).width()<991.99)?34:84,
                    }

                },
                {
                    id: 'right-y-axis',
                    type: 'linear',
                    position: 'right',
                    ticks: {
                        display : false,
                        beginAtZero: false,
                        fontSize: ($(window).width()<991.99)?10:14,

                    },
                    gridLines: {
                        display : false,
                        drawBorder: false,
                        tickMarkLength:  ($(window).width()<991.99)?34:84,
                    }
                }
            ]
        }
    }
};


window.onload = function() {

var data_string='{"success":true,"axis":["Пн","Вт","Ср","Чт","Пт","Сб","Вс"],"data":{"TempOutdoor":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":-4.9787234042553195,"Вт":-2.9166666666666665,"Ср":-3.3125,"Чт":2.5208333333333335,"Пт":6.84375,"Сб":0,"Вс":0},"min":"-4.98","max":"6.84","avg":"-0.26","sum":"-1.84","name":"Температура на улице","color":"#60AD5E","value_type":"instant"},"MotoHW":{"period_start":"2021-05-10 00:00:00","period_end":"2021-05-16 23:59:59","data":{"Пн":11,"Вт":15,"Ср":13,"Чт":12,"Пт":9,"Сб":0,"Вс":0},"min":"0.00","max":"15.00","avg":"8.57","sum":"60.00","name":"Мотогодини: Гаряча вода","color":"#29819D","value_type":"counter"}},"closestPeriods":{"previous":{"2021-05-03 00:00:00":"03.05 - 09.05"},"current":{"2021-05-10 00:00:00":"10.05 - 16.05"},"next":null}}';
  var data = JSON.parse(data_string);    

                config.data.labels = data.axis;
                var values_obj = data.data;
                data_generation(values_obj);
                //console.log(JSON.stringify(config));
                var ctx = document.getElementById('StatisticsChartCanvas').getContext('2d');
                window.StatisticsChart = new Chart(ctx,config); 

};
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.3/Chart.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="chart-wrapper" style="width:548px; height:265px;">
  <canvas id="StatisticsChartCanvas"></canvas>
</div>


推荐阅读