Make the X-Axis labels in chart.js increment by a certain scale
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.