Диаграмма. js настройка, наложение двух осей Y, заполнение области диаграммы, заполнение нечетных отметок - PullRequest
0 голосов
/ 10 июля 2020
Плагин

The Chart. js супер, отличный, красивый и легко настраиваемый. Но даже все это не помогло мне решить несколько проблем.

Мне нужно создать диаграмму с идеальными пикселями в соответствии с дизайном, показанным на рисунке 1

Надеюсь на вашу помощь !!

  1. Как сделать строки без подписей длиннее , чем с подписями
  2. Сделал отступ от шкал до графиков с помощью параметр tickMarkLength , но, может быть, можно как-то под другим, потому что видно перекрытие одного масштаба на другой.
  3. Как сделать, чтобы линии сетки левого и правого масштабов совпадали ? Я установил beforeUpdate .stepSize, но не смотря на то, что я указываю, что должно быть 8 интервалов, иногда 8, затем 9.

image

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>

1 Ответ

0 голосов
/ 11 июля 2020

После огромного количества идей, попыток и ошибок, я нашел решение для полной настройки шкалы.

Для этого я

  1. Отключено отображение линий сетки и отметок с помощью свойства
    display:false,
Написал плагин , который рисует линии сетки и отметки в соответствии с дизайном. Вот что случилось

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>
...