Chart Js 更改 Ox 轴上的文本标签方向

Chart Js change text label orientation on Ox axis

我正在使用 chartJS 库 - http://www.chartjs.org/

如果文字标签过大,chartJs 45度显示文字 请告诉我如何将文本旋转更改为 30 度。

请看以下内容:

    var data = {   labels:['Large Text Very Large','Text1','Text2'],
                 datasets:[ { label:'DS1',
                              fillColor:'rgba(228,218,86,0.5)',  
                             strokeColor:'rgba(228,218,86,0.8)',
                              highlightFill:'rgba(228,218,86,0.75)',
                              highlightStroke: 'rgba(228,218,86,1)', data:[0,1,0]
                            }
                 ]
           }


    var options = {
        animation: false
    };

    //Get the context of the canvas element we want to select
    var c = $('#myChart');
    var ct = c.get(0).getContext('2d');
    var ctx = document.getElementById("myChart").getContext("2d");
    /*************************************************************************/
    myNewChart = new Chart(ct).Bar(data, options);

也在 jsfiddle 上:

http://jsfiddle.net/cvL9fk2t/

谢谢!

您需要扩展比例尺 class 并覆盖 calculateXLabelRotation 方法以使用用户输入的旋转而不是尝试自行计算。如果这样做,则需要扩展条形图或折线图并覆盖 init 方法以使用此比例 class。 (或者您可以直接对刻度、条形和线条进行这些更改 classes,然后无需覆盖)。

所以首先扩展规模 class 并使其使用用户定义的选项

var helpers = Chart.helpers;
Chart.MyScale = Chart.Scale.extend({
    calculateXLabelRotation: function() {
        //Get the width of each grid by calculating the difference
        //between x offsets between 0 and 1.

        this.ctx.font = this.font;

        var firstWidth = this.ctx.measureText(this.xLabels[0]).width,
            lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width,
            firstRotated,
            lastRotated;


        this.xScalePaddingRight = lastWidth / 2 + 3;
        this.xScalePaddingLeft = (firstWidth / 2 > this.yLabelWidth + 10) ? firstWidth / 2 : this.yLabelWidth + 10;

        this.xLabelRotation = 0;
        if (this.display) {
            var originalLabelWidth = helpers.longestText(this.ctx, this.font, this.xLabels),
                cosRotation,
                firstRotatedWidth;
            this.xLabelWidth = originalLabelWidth;
            //Allow 3 pixels x2 padding either side for label readability
            var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6;
            //check if option is set if so use that
            if (this.overrideRotation) {
                // do the same as before but manualy set the rotation rather than looping
                 this.xLabelRotation = this.overrideRotation;
                 cosRotation = Math.cos(helpers.radians(this.xLabelRotation));
                  // We're right aligning the text now.
                    if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8) {
                        this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
                    }
                    this.xScalePaddingRight = this.fontSize / 2;
                    this.xLabelWidth = cosRotation * originalLabelWidth;
            } else {
                //Max label rotate should be 90 - also act as a loop counter
                while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)) {
                    cosRotation = Math.cos(helpers.radians(this.xLabelRotation));

                    firstRotated = cosRotation * firstWidth;
                    lastRotated = cosRotation * lastWidth;

                    // We're right aligning the text now.
                    if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8) {
                        this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
                    }
                    this.xScalePaddingRight = this.fontSize / 2;


                    this.xLabelRotation++;
                    this.xLabelWidth = cosRotation * originalLabelWidth;

                }
            }
            if (this.xLabelRotation > 0) {
                this.endPoint -= Math.sin(helpers.radians(this.xLabelRotation)) * originalLabelWidth + 3;
            }
        } else {
            this.xLabelWidth = 0;
            this.xScalePaddingRight = this.padding;
            this.xScalePaddingLeft = this.padding;
        }

    },

});

然后在扩展栏中 class 创建一个新的图形类型并覆盖 init 方法以使用新的

Chart.types.Bar.extend({
    name: "MyBar",
    initialize: function(data) {

        //Expose options as a scope variable here so we can access it in the ScaleClass
        var options = this.options;

        this.ScaleClass = Chart.MyScale.extend({
            overrideRotation: options.overrideRotation,
            offsetGridLines: true,
            calculateBarX: function(datasetCount, datasetIndex, barIndex) {
                //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
                var xWidth = this.calculateBaseWidth(),
                    xAbsolute = this.calculateX(barIndex) - (xWidth / 2),
                    barWidth = this.calculateBarWidth(datasetCount);

                return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth / 2;
            },
            calculateBaseWidth: function() {
                return (this.calculateX(1) - this.calculateX(0)) - (2 * options.barValueSpacing);
            },
            calculateBarWidth: function(datasetCount) {
                //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
                var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);

                return (baseWidth / datasetCount);
            }
        });

        this.datasets = [];

        //Set up tooltip events on the chart
        if (this.options.showTooltips) {
            helpers.bindEvents(this, this.options.tooltipEvents, function(evt) {
                var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];

                this.eachBars(function(bar) {
                    bar.restore(['fillColor', 'strokeColor']);
                });
                helpers.each(activeBars, function(activeBar) {
                    activeBar.fillColor = activeBar.highlightFill;
                    activeBar.strokeColor = activeBar.highlightStroke;
                });
                this.showTooltip(activeBars);
            });
        }

        //Declare the extension of the default point, to cater for the options passed in to the constructor
        this.BarClass = Chart.Rectangle.extend({
            strokeWidth: this.options.barStrokeWidth,
            showStroke: this.options.barShowStroke,
            ctx: this.chart.ctx
        });

        //Iterate through each of the datasets, and build this into a property of the chart
        helpers.each(data.datasets, function(dataset, datasetIndex) {

            var datasetObject = {
                label: dataset.label || null,
                fillColor: dataset.fillColor,
                strokeColor: dataset.strokeColor,
                bars: []
            };

            this.datasets.push(datasetObject);

            helpers.each(dataset.data, function(dataPoint, index) {
                //Add a new point for each piece of data, passing any required data to draw.
                datasetObject.bars.push(new this.BarClass({
                    value: dataPoint,
                    label: data.labels[index],
                    datasetLabel: dataset.label,
                    strokeColor: dataset.strokeColor,
                    fillColor: dataset.fillColor,
                    highlightFill: dataset.highlightFill || dataset.fillColor,
                    highlightStroke: dataset.highlightStroke || dataset.strokeColor
                }));
            }, this);

        }, this);

        this.buildScale(data.labels);

        this.BarClass.prototype.base = this.scale.endPoint;

        this.eachBars(function(bar, index, datasetIndex) {
            helpers.extend(bar, {
                width: this.scale.calculateBarWidth(this.datasets.length),
                x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
                y: this.scale.endPoint
            });
            bar.save();
        }, this);

        this.render();
    },
});

现在您可以使用此图表类型声明图表并传入选项 overrideRotation

这里有一个 fiddle 例子 http://jsfiddle.net/leighking2/ye3usuhu/

和一个片段

var helpers = Chart.helpers;
Chart.MyScale = Chart.Scale.extend({
    calculateXLabelRotation: function() {
        //Get the width of each grid by calculating the difference
        //between x offsets between 0 and 1.

        this.ctx.font = this.font;

        var firstWidth = this.ctx.measureText(this.xLabels[0]).width,
            lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width,
            firstRotated,
            lastRotated;


        this.xScalePaddingRight = lastWidth / 2 + 3;
        this.xScalePaddingLeft = (firstWidth / 2 > this.yLabelWidth + 10) ? firstWidth / 2 : this.yLabelWidth + 10;

        this.xLabelRotation = 0;
        if (this.display) {
            var originalLabelWidth = helpers.longestText(this.ctx, this.font, this.xLabels),
                cosRotation,
                firstRotatedWidth;
            this.xLabelWidth = originalLabelWidth;
            //Allow 3 pixels x2 padding either side for label readability
            var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6;
            
            if (this.overrideRotation) {
                 this.xLabelRotation = this.overrideRotation;
                 cosRotation = Math.cos(helpers.radians(this.xLabelRotation));
                  // We're right aligning the text now.
                    if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8) {
                        this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
                    }
                    this.xScalePaddingRight = this.fontSize / 2;
                    this.xLabelWidth = cosRotation * originalLabelWidth;
            } else {
                //Max label rotate should be 90 - also act as a loop counter
                while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)) {
                    cosRotation = Math.cos(helpers.radians(this.xLabelRotation));

                    firstRotated = cosRotation * firstWidth;
                    lastRotated = cosRotation * lastWidth;

                    // We're right aligning the text now.
                    if (firstRotated + this.fontSize / 2 > this.yLabelWidth + 8) {
                        this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
                    }
                    this.xScalePaddingRight = this.fontSize / 2;


                    this.xLabelRotation++;
                    this.xLabelWidth = cosRotation * originalLabelWidth;

                }
            }
            if (this.xLabelRotation > 0) {
                this.endPoint -= Math.sin(helpers.radians(this.xLabelRotation)) * originalLabelWidth + 3;
            }
        } else {
            this.xLabelWidth = 0;
            this.xScalePaddingRight = this.padding;
            this.xScalePaddingLeft = this.padding;
        }

    },

});

Chart.types.Bar.extend({
    name: "MyBar",
    initialize: function(data) {

        //Expose options as a scope variable here so we can access it in the ScaleClass
        var options = this.options;

        this.ScaleClass = Chart.MyScale.extend({
            overrideRotation: options.overrideRotation,
            offsetGridLines: true,
            calculateBarX: function(datasetCount, datasetIndex, barIndex) {
                //Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
                var xWidth = this.calculateBaseWidth(),
                    xAbsolute = this.calculateX(barIndex) - (xWidth / 2),
                    barWidth = this.calculateBarWidth(datasetCount);

                return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth / 2;
            },
            calculateBaseWidth: function() {
                return (this.calculateX(1) - this.calculateX(0)) - (2 * options.barValueSpacing);
            },
            calculateBarWidth: function(datasetCount) {
                //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
                var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);

                return (baseWidth / datasetCount);
            }
        });

        this.datasets = [];

        //Set up tooltip events on the chart
        if (this.options.showTooltips) {
            helpers.bindEvents(this, this.options.tooltipEvents, function(evt) {
                var activeBars = (evt.type !== 'mouseout') ? this.getBarsAtEvent(evt) : [];

                this.eachBars(function(bar) {
                    bar.restore(['fillColor', 'strokeColor']);
                });
                helpers.each(activeBars, function(activeBar) {
                    activeBar.fillColor = activeBar.highlightFill;
                    activeBar.strokeColor = activeBar.highlightStroke;
                });
                this.showTooltip(activeBars);
            });
        }

        //Declare the extension of the default point, to cater for the options passed in to the constructor
        this.BarClass = Chart.Rectangle.extend({
            strokeWidth: this.options.barStrokeWidth,
            showStroke: this.options.barShowStroke,
            ctx: this.chart.ctx
        });

        //Iterate through each of the datasets, and build this into a property of the chart
        helpers.each(data.datasets, function(dataset, datasetIndex) {

            var datasetObject = {
                label: dataset.label || null,
                fillColor: dataset.fillColor,
                strokeColor: dataset.strokeColor,
                bars: []
            };

            this.datasets.push(datasetObject);

            helpers.each(dataset.data, function(dataPoint, index) {
                //Add a new point for each piece of data, passing any required data to draw.
                datasetObject.bars.push(new this.BarClass({
                    value: dataPoint,
                    label: data.labels[index],
                    datasetLabel: dataset.label,
                    strokeColor: dataset.strokeColor,
                    fillColor: dataset.fillColor,
                    highlightFill: dataset.highlightFill || dataset.fillColor,
                    highlightStroke: dataset.highlightStroke || dataset.strokeColor
                }));
            }, this);

        }, this);

        this.buildScale(data.labels);

        this.BarClass.prototype.base = this.scale.endPoint;

        this.eachBars(function(bar, index, datasetIndex) {
            helpers.extend(bar, {
                width: this.scale.calculateBarWidth(this.datasets.length),
                x: this.scale.calculateBarX(this.datasets.length, datasetIndex, index),
                y: this.scale.endPoint
            });
            bar.save();
        }, this);

        this.render();
    },
});



var randomScalingFactor = function() {
    return Math.round(Math.random() * 100)
};

var barChartData = {
    labels: ["January", "February", "March", "April", "May", "June", "July"],
    datasets: [{
        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: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
    }, {
        fillColor: "rgba(151,187,205,0.5)",
        strokeColor: "rgba(151,187,205,0.8)",
        highlightFill: "rgba(151,187,205,0.75)",
        highlightStroke: "rgba(151,187,205,1)",
        data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
    }, {
        fillColor: "rgba(15,18,20,0.5)",
        strokeColor: "rgba(15,18,20,0.8)",
        highlightFill: "rgba(15,18,20,0.75)",
        highlightStroke: "rgba(15,18,20,1)",
        data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
    }]

}
window.onload = function() {
    var ctx = document.getElementById("canvas").getContext("2d");
    window.myBar = new Chart(ctx).MyBar(barChartData, {
        overrideRotation: 30
    });
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.1/Chart.js"></script>

    <canvas id="canvas" height="150" width="300"></canvas>

如果您使用的是chart.js 2.x,只需设置 maxRotation: 90minRotation: 90刻度选项。这个对我有用! 如果你想要所有的 x 标签,你可能想要设置 autoSkip: false。 下面是一个例子。

var myChart = new Chart(ctx, {
  type: 'bar',
  data: chartData,
  options: {
    scales: {
      xAxes: [{
        ticks: {
          autoSkip: false,
          maxRotation: 90,
          minRotation: 90
        }
      }]
    }
  }
});

请注意,对于 chart.js 3.x 指定轴比例选项的方式已更改:请参阅 https://www.chartjs.org/docs/master/getting-started/v3-migration.html#scales

因此,在上述 2.x 的答案中,您需要像这样删除方括号:

var myChart = new Chart(ctx, {
  type: 'bar',
  data: chartData,
  options: {
    scales: {
      xAxes: {
        ticks: {
          autoSkip: false,
          maxRotation: 90,
          minRotation: 90
        }
      }
    }
  }
});