Make the X-Axis labels in chart.js increment by a certain scale

前端 未结 2 1323
忘掉有多难
忘掉有多难 2020-12-15 23:39

I have labels ranging from 50-90, and every number in between in displayed.

I would like to list the labels by 5 or 10 because currently they are all crunched toget

相关标签:
2条回答
  • 2020-12-16 00:10

    I updated the provided code snippet to prevent the rotation of the x-axis labels. Also some parameters were not being passed on in the constructor. Check the //Mike Walder comments.

     //Code to manually set the interval of X-Axis Labels: From http://jsfiddle.net/leighking2/n9c8jx55/
            Chart.CustomScale = Chart.Scale.extend({
                draw: function () {
                    var helpers = Chart.helpers;
                    var each = helpers.each;
                    var aliasPixel = helpers.aliasPixel;
                    var toRadians = helpers.radians;
                    var ctx = this.ctx,
                        yLabelGap = (this.endPoint - this.startPoint) / this.steps,
                        xStart = Math.round(this.xScalePaddingLeft);
                    if (this.display) {
                        ctx.fillStyle = this.textColor;
                        ctx.font = this.font;
                        each(this.yLabels, function (labelString, index) {
                            var yLabelCenter = this.endPoint - (yLabelGap * index),
                                linePositionY = Math.round(yLabelCenter);
    
                            ctx.textAlign = "right";
                            ctx.textBaseline = "middle";
                            if (this.showLabels) {
                                ctx.fillText(labelString, xStart - 10, yLabelCenter);
                            }
                            ctx.beginPath();
                            if (index > 0) {
                                // This is a grid line in the centre, so drop that
                                ctx.lineWidth = this.gridLineWidth;
                                ctx.strokeStyle = this.gridLineColor;
                            } else {
                                // This is the first line on the scale
                                ctx.lineWidth = this.lineWidth;
                                ctx.strokeStyle = this.lineColor;
                            }
    
                            linePositionY += helpers.aliasPixel(ctx.lineWidth);
    
                            ctx.moveTo(xStart, linePositionY);
                            ctx.lineTo(this.width, linePositionY);
                            ctx.stroke();
                            ctx.closePath();
    
                            ctx.lineWidth = this.lineWidth;
                            ctx.strokeStyle = this.lineColor;
                            ctx.beginPath();
                            ctx.moveTo(xStart - 5, linePositionY);
                            ctx.lineTo(xStart, linePositionY);
                            ctx.stroke();
                            ctx.closePath();
    
                        }, this);
    
                        each(this.xLabels, function (label, index) {
                            //======================================================
                            //apply the filter to the index if it is a function
                            //======================================================
                            if (typeof this.labelsFilter === "function" && this.labelsFilter(index)) {
                                return;
                            }
                            //Hack by Mike Walder to enforce X-Labels are Written horizontally
                            var xLabelRot = this.xLabelRotation;
                            this.xLabelRotation = 0;
                            //End of Hack
                            var xPos = this.calculateX(index) + aliasPixel(this.lineWidth),
                                // Check to see if line/bar here and decide where to place the line
                                linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth),
                                //Mike Walder: isRotated nees original Roation Value to display the X-Label in the RollOver Area of a Datapoint
                                isRotated = true;(xLabelRot > 0);
    
                            ctx.beginPath();
                        if(this.scaleShowVerticalLines){
                                if (index > 0) {
                                    // This is a grid line in the centre, so drop that
                                    ctx.lineWidth = this.gridLineWidth;
                                    ctx.strokeStyle = this.gridLineColor;
                                } else {
                                    // This is the first line on the scale
                                    ctx.lineWidth = this.lineWidth;
                                    ctx.strokeStyle = this.lineColor;
                                }
                                ctx.moveTo(linePos, this.endPoint);
                                ctx.lineTo(linePos, this.startPoint - 3);
                                ctx.stroke();
                                ctx.closePath();
    
    
                                ctx.lineWidth = this.lineWidth;
                                ctx.strokeStyle = this.lineColor;
                            }
                            // Small lines at the bottom of the base grid line
                            ctx.beginPath();
                            ctx.moveTo(linePos, this.endPoint);
                            ctx.lineTo(linePos, this.endPoint + 5);
                            ctx.stroke();
                            ctx.closePath();
    
                            ctx.save();
                            ctx.translate(xPos, (isRotated) ? this.endPoint + 12 : this.endPoint + 8);
                            ctx.rotate(toRadians(this.xLabelRotation) * -1);
    
                            //Mike Walder added center here, because it looks better if the label designator is in the center of the smal line
                            ctx.textAlign = "center";
                            ctx.textBaseline = (isRotated) ? "middle" : "top";
                            ctx.fillText(label, 0, 0);
                            ctx.restore();
    
                        }, this);
    
                    }
                }
            });
    
    Chart.types.Line.extend({
                name: "LineAlt",
                initialize: function (data) {
                    //======================================================
                    //ensure the new option is part of the options
                    //======================================================
                    this.options.labelsFilter = data.labelsFilter || null;
                    Chart.types.Line.prototype.initialize.apply(this, arguments);
    
                },
                buildScale: function (labels) {
                    var helpers = Chart.helpers;
                    var self = this;
    
                    var dataTotal = function () {
                        var values = [];
                        self.eachPoints(function (point) {
                            values.push(point.value);
                        });
    
                        return values;
                    };
                    var scaleOptions = {
                        // Mike Walder: added this configuration option since it is overridden in the new code
                        scaleShowVerticalLines: this.options.scaleShowVerticalLines,
                        templateString: this.options.scaleLabel,
                        height: this.chart.height,
                        width: this.chart.width,
                        ctx: this.chart.ctx,
                        textColor: this.options.scaleFontColor,
                        fontSize: this.options.scaleFontSize,
                        //======================================================
                        //pass this new options to the scale object
                        //======================================================
                        labelsFilter: this.options.labelsFilter,
                        fontStyle: this.options.scaleFontStyle,
                        fontFamily: this.options.scaleFontFamily,
                        valuesCount: labels.length,
                        beginAtZero: this.options.scaleBeginAtZero,
                        integersOnly: this.options.scaleIntegersOnly,
                        calculateYRange: function (currentHeight) {
                            var updatedRanges = helpers.calculateScaleRange(
                            dataTotal(),
                            currentHeight,
                            this.fontSize,
                            this.beginAtZero,
                            this.integersOnly);
                            helpers.extend(this, updatedRanges);
                        },
                        xLabels: labels,
                        font: helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
                        lineWidth: this.options.scaleLineWidth,
                        lineColor: this.options.scaleLineColor,
                        gridLineWidth: (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
                        gridLineColor: (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
                        padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
                        showLabels: this.options.scaleShowLabels,
                        display: this.options.showScale
                    };
    
                    if (this.options.scaleOverride) {
                        helpers.extend(scaleOptions, {
                            calculateYRange: helpers.noop,
                            steps: this.options.scaleSteps,
                            stepValue: this.options.scaleStepWidth,
                            min: this.options.scaleStartValue,
                            max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
                        });
                    }
    
                    //======================================================
                    //Use the new Custom Scal that will make use of a labelsFilter function
                    //======================================================
                    this.scale = new Chart.CustomScale(scaleOptions);
                }
            });
    
    0 讨论(0)
  • 2020-12-16 00:27

    EDIT 2: Ok so i actually needed functionality like this in a project I am working on so i have made a custom build of chart.js to include this functionality.http://jsfiddle.net/leighking2/mea767ss/ or https://github.com/leighquince/Chart.js

    It's a combination of the two solutions below but tied into the core of CHart.js so no need to specify custom scale and charts.

    Both line and bar charts have a new option called

    labelsFilter:function(label, index){return false;)
    

    by default this will just return false so all labels on the x-axis will display but if a filter is passed as an option then it will filter the labels

    so here is an example with both bar and line

    var ctx = document.getElementById("chart").getContext("2d");
    var data = {
        labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
        datasets: [{
            label: "My First dataset",
            fillColor: "rgba(220,220,220,0.5)",
            strokeColor: "rgba(220,220,220,0.8)",
            highlightFill: "rgba(220,220,220,0.75)",
            highlightStroke: "rgba(220,220,220,1)",
    
            data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
        }]
    };
    
    
    var myLineChart = new Chart(ctx).Line(data, {
        labelsFilter: function (value, index) {
            return (index + 1) % 5 !== 0;
        }
    });
    <script src="http://quincewebdesign.com/cdn/Chart.js"></script>
    <canvas id="chart" width="1200px"></canvas>


    ORIGINAL ANSWER

    You can override the scale draw function to achieve this. Only thing i don;t like about this is it will apply to all your graphs so the other option is to have a custom graph type that makes use of the overridden draw.

    EDIT 1: just realized the same affect can be achieved by using the index value rather than the label and this can then be applied to all label types not just numerical ones, this applies for both examples and could be easily changed. Here is the second example using the index rather than the label http://jsfiddle.net/leighking2/n9c8jx55/

    1st - Overriding the scale draw function http://jsfiddle.net/leighking2/96grgz0d/

    Only change here is before drawing the x-axis label we test if the label is a number and if its remainder when divided by 5 is not equal to 0 (so any number not dividable by 5) if it matches both those criteria we do not draw the label

    Chart.Scale = Chart.Scale.extend({
       draw : function(){
               console.log(this);
               var helpers = Chart.helpers;
               var each = helpers.each;
               var aliasPixel = helpers.aliasPixel;
                  var toRadians = helpers.radians;
                var ctx = this.ctx,
                    yLabelGap = (this.endPoint - this.startPoint) / this.steps,
                    xStart = Math.round(this.xScalePaddingLeft);
                if (this.display){
                    ctx.fillStyle = this.textColor;
                    ctx.font = this.font;
                    each(this.yLabels,function(labelString,index){
                        var yLabelCenter = this.endPoint - (yLabelGap * index),
                            linePositionY = Math.round(yLabelCenter);
    
                        ctx.textAlign = "right";
                        ctx.textBaseline = "middle";
                        if (this.showLabels){
                            ctx.fillText(labelString,xStart - 10,yLabelCenter);
                        }
                        ctx.beginPath();
                        if (index > 0){
                            // This is a grid line in the centre, so drop that
                            ctx.lineWidth = this.gridLineWidth;
                            ctx.strokeStyle = this.gridLineColor;
                        } else {
                            // This is the first line on the scale
                            ctx.lineWidth = this.lineWidth;
                            ctx.strokeStyle = this.lineColor;
                        }
    
                        linePositionY += helpers.aliasPixel(ctx.lineWidth);
    
                        ctx.moveTo(xStart, linePositionY);
                        ctx.lineTo(this.width, linePositionY);
                        ctx.stroke();
                        ctx.closePath();
    
                        ctx.lineWidth = this.lineWidth;
                        ctx.strokeStyle = this.lineColor;
                        ctx.beginPath();
                        ctx.moveTo(xStart - 5, linePositionY);
                        ctx.lineTo(xStart, linePositionY);
                        ctx.stroke();
                        ctx.closePath();
    
                    },this);
    
                    each(this.xLabels,function(label,index){
                        //================================
                        //test to see if we draw the label
                        //================================
                        if(typeof label === "number" && label%5 != 0){
                         return;   
                        }
                        var xPos = this.calculateX(index) + aliasPixel(this.lineWidth),
                            // Check to see if line/bar here and decide where to place the line
                            linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth),
                            isRotated = (this.xLabelRotation > 0);
    
                        ctx.beginPath();
    
                        if (index > 0){
                            // This is a grid line in the centre, so drop that
                            ctx.lineWidth = this.gridLineWidth;
                            ctx.strokeStyle = this.gridLineColor;
                        } else {
                            // This is the first line on the scale
                            ctx.lineWidth = this.lineWidth;
                            ctx.strokeStyle = this.lineColor;
                        }
                        ctx.moveTo(linePos,this.endPoint);
                        ctx.lineTo(linePos,this.startPoint - 3);
                        ctx.stroke();
                        ctx.closePath();
    
    
                        ctx.lineWidth = this.lineWidth;
                        ctx.strokeStyle = this.lineColor;
    
    
                        // Small lines at the bottom of the base grid line
                        ctx.beginPath();
                        ctx.moveTo(linePos,this.endPoint);
                        ctx.lineTo(linePos,this.endPoint + 5);
                        ctx.stroke();
                        ctx.closePath();
    
                        ctx.save();
                        ctx.translate(xPos,(isRotated) ? this.endPoint + 12 : this.endPoint + 8);
                        ctx.rotate(toRadians(this.xLabelRotation)*-1);
    
                        ctx.textAlign = (isRotated) ? "right" : "center";
                        ctx.textBaseline = (isRotated) ? "middle" : "top";
                        ctx.fillText(label, 0, 0);
                        ctx.restore();
    
                    },this);
    
                }
            } 
    });
    

    then we can use the graphs like normal. Declare data

    var ctx = document.getElementById("chart").getContext("2d");
    var data = {
        labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
        datasets: [{
            label: "My First dataset",
            fillColor: "rgba(220,220,220,0.2)",
            strokeColor: "rgba(220,220,220,1)",
            pointColor: "rgba(220,220,220,1)",
            pointStrokeColor: "#fff",
            pointHighlightFill: "#fff",
            pointHighlightStroke: "rgba(220,220,220,1)",
            data: [65, 34, 21, 11, 11, 34, 34, 12, 24, 45, 65, 34, 21, 11, 11, 34, 34, 12, 24, 45, 65, 34, 21, 11, 11, 34, 34, 12, 24, 45]
        }, ]
    };
    

    draw graph

    var myLineChart = new Chart(ctx).Line(data);
    

    2nd custom graph + custom scale + filter function http://jsfiddle.net/leighking2/6xej5ek3/

    In this method we still need to create a custom scale object but instead of having this applied to all charts we create we can choose to only apply it to those that we have declared. Also in this example we can also have the filter be a function that gets applied at run time so we can have each graph filter the labels differently

    first the scale object

    Chart.CustomScale = Chart.Scale.extend({
        draw: function () {
            console.log(this);
            var helpers = Chart.helpers;
            var each = helpers.each;
            var aliasPixel = helpers.aliasPixel;
            var toRadians = helpers.radians;
            var ctx = this.ctx,
                yLabelGap = (this.endPoint - this.startPoint) / this.steps,
                xStart = Math.round(this.xScalePaddingLeft);
            if (this.display) {
                ctx.fillStyle = this.textColor;
                ctx.font = this.font;
                each(this.yLabels, function (labelString, index) {
                    var yLabelCenter = this.endPoint - (yLabelGap * index),
                        linePositionY = Math.round(yLabelCenter);
    
                    ctx.textAlign = "right";
                    ctx.textBaseline = "middle";
                    if (this.showLabels) {
                        ctx.fillText(labelString, xStart - 10, yLabelCenter);
                    }
                    ctx.beginPath();
                    if (index > 0) {
                        // This is a grid line in the centre, so drop that
                        ctx.lineWidth = this.gridLineWidth;
                        ctx.strokeStyle = this.gridLineColor;
                    } else {
                        // This is the first line on the scale
                        ctx.lineWidth = this.lineWidth;
                        ctx.strokeStyle = this.lineColor;
                    }
    
                    linePositionY += helpers.aliasPixel(ctx.lineWidth);
    
                    ctx.moveTo(xStart, linePositionY);
                    ctx.lineTo(this.width, linePositionY);
                    ctx.stroke();
                    ctx.closePath();
    
                    ctx.lineWidth = this.lineWidth;
                    ctx.strokeStyle = this.lineColor;
                    ctx.beginPath();
                    ctx.moveTo(xStart - 5, linePositionY);
                    ctx.lineTo(xStart, linePositionY);
                    ctx.stroke();
                    ctx.closePath();
    
                }, this);
    
                each(this.xLabels, function (label, index) {
                    //======================================================
                    //apply the filter the the label if it is a function
                    //======================================================
                    if (typeof this.labelsFilter === "function" && this.labelsFilter(label)) {
                        return;
                    }
                    var xPos = this.calculateX(index) + aliasPixel(this.lineWidth),
                        // Check to see if line/bar here and decide where to place the line
                        linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth),
                        isRotated = (this.xLabelRotation > 0);
    
                    ctx.beginPath();
    
                    if (index > 0) {
                        // This is a grid line in the centre, so drop that
                        ctx.lineWidth = this.gridLineWidth;
                        ctx.strokeStyle = this.gridLineColor;
                    } else {
                        // This is the first line on the scale
                        ctx.lineWidth = this.lineWidth;
                        ctx.strokeStyle = this.lineColor;
                    }
                    ctx.moveTo(linePos, this.endPoint);
                    ctx.lineTo(linePos, this.startPoint - 3);
                    ctx.stroke();
                    ctx.closePath();
    
    
                    ctx.lineWidth = this.lineWidth;
                    ctx.strokeStyle = this.lineColor;
    
    
                    // Small lines at the bottom of the base grid line
                    ctx.beginPath();
                    ctx.moveTo(linePos, this.endPoint);
                    ctx.lineTo(linePos, this.endPoint + 5);
                    ctx.stroke();
                    ctx.closePath();
    
                    ctx.save();
                    ctx.translate(xPos, (isRotated) ? this.endPoint + 12 : this.endPoint + 8);
                    ctx.rotate(toRadians(this.xLabelRotation) * -1);
    
                    ctx.textAlign = (isRotated) ? "right" : "center";
                    ctx.textBaseline = (isRotated) ? "middle" : "top";
                    ctx.fillText(label, 0, 0);
                    ctx.restore();
    
                }, this);
    
            }
        }
    });
    

    now the custom graph that will make use of this scale, rather annoyingly we have to override the whole of the buildscale function

    Chart.types.Line.extend({
        name: "LineAlt",
        initialize: function (data) {
            //======================================================
            //ensure the new option is part of the options
            //======================================================
            this.options.labelsFilter = data.labelsFilter || null;
            Chart.types.Line.prototype.initialize.apply(this, arguments);
    
    
        },
        buildScale: function (labels) {
            var helpers = Chart.helpers;
            var self = this;
    
            var dataTotal = function () {
                var values = [];
                self.eachPoints(function (point) {
                    values.push(point.value);
                });
    
                return values;
            };
            var scaleOptions = {
                templateString: this.options.scaleLabel,
                height: this.chart.height,
                width: this.chart.width,
                ctx: this.chart.ctx,
                textColor: this.options.scaleFontColor,
                fontSize: this.options.scaleFontSize,
                //======================================================
                //pass this new options to the scale object
                //======================================================
                labelsFilter: this.options.labelsFilter,
                fontStyle: this.options.scaleFontStyle,
                fontFamily: this.options.scaleFontFamily,
                valuesCount: labels.length,
                beginAtZero: this.options.scaleBeginAtZero,
                integersOnly: this.options.scaleIntegersOnly,
                calculateYRange: function (currentHeight) {
                    var updatedRanges = helpers.calculateScaleRange(
                    dataTotal(),
                    currentHeight,
                    this.fontSize,
                    this.beginAtZero,
                    this.integersOnly);
                    helpers.extend(this, updatedRanges);
                },
                xLabels: labels,
                font: helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
                lineWidth: this.options.scaleLineWidth,
                lineColor: this.options.scaleLineColor,
                gridLineWidth: (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
                gridLineColor: (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
                padding: (this.options.showScale) ? 0 : this.options.pointDotRadius + this.options.pointDotStrokeWidth,
                showLabels: this.options.scaleShowLabels,
                display: this.options.showScale
            };
    
            if (this.options.scaleOverride) {
                helpers.extend(scaleOptions, {
                    calculateYRange: helpers.noop,
                    steps: this.options.scaleSteps,
                    stepValue: this.options.scaleStepWidth,
                    min: this.options.scaleStartValue,
                    max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
                });
            }
    
            //======================================================
            //Use the new Custom Scal that will make use of a labelsFilter function
            //======================================================
            this.scale = new Chart.CustomScale(scaleOptions);
        }
    });
    

    then we can use it like normal. Declare the data but this time pass a new option for labelsFilter that is a function to apply the filtering of the x labels

    var ctx = document.getElementById("chart").getContext("2d");
    var data = {
        labels: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30],
        labelsFilter: function (label) {
            //return true if this label should be filtered out
            return label % 5 !== 0;
        },
        datasets: [{
            label: "My First dataset",
            fillColor: "rgba(220,220,220,0.2)",
            strokeColor: "rgba(220,220,220,1)",
            pointColor: "rgba(220,220,220,1)",
            pointStrokeColor: "#fff",
            pointHighlightFill: "#fff",
            pointHighlightStroke: "rgba(220,220,220,1)",
            data: [65, 34, 21, 11, 11, 34, 34, 12, 24, 45, 65, 34, 21, 11, 11, 34, 34, 12, 24, 45, 65, 34, 21, 11, 11, 34, 34, 12, 24, 45]
        }, ]
    };
    

    then draw the graph using our new custom graph name

    var myLineChart = new Chart(ctx).LineAlt(data);
    

    Overall even though it is a bit more involved i prefer the second method as it means that a custom filter can be applied to each graph i declare.

    0 讨论(0)
提交回复
热议问题