--- |:---:| --- | ---
type | String | Chart specific. | Type of scale being employed. Custom scales can be created and registered with a string key. Options: ["category"](#scales-category-scale), ["linear"](#scales-linear-scale), ["logarithmic"](#scales-logarithmic-scale), ["time"](#scales-time-scale), ["radialLinear"](#scales-radial-linear-scale)
display | Boolean | true | If true, show the scale including gridlines, ticks, and labels. Overrides *gridLines.display*, *scaleLabel.display*, and *ticks.display*.
+beforeUpdate | Function | undefined | Callback called before the update process starts. Passed a single argument, the scale instance.
+beforeSetDimensions | Function | undefined | Callback that runs before dimensions are set. Passed a single argument, the scale instance.
+afterSetDimensions | Function | undefined | Callback that runs after dimensions are set. Passed a single argument, the scale instance.
+beforeDataLimits | Function | undefined | Callback that runs before data limits are determined. Passed a single argument, the scale instance.
+afterDataLimits | Function | undefined | Callback that runs after data limits are determined. Passed a single argument, the scale instance.
+beforeBuildTicks | Function | undefined | Callback that runs before ticks are created. Passed a single argument, the scale instance.
+afterBuildTicks | Function | undefined | Callback that runs after ticks are created. Useful for filtering ticks. Passed a single argument, the scale instance.
+beforeTickToLabelConversion | Function | undefined | Callback that runs before ticks are converted into strings. Passed a single argument, the scale instance.
+afterTickToLabelConversion | Function | undefined | Callback that runs after ticks are converted into strings. Passed a single argument, the scale instance.
+beforeCalculateTickRotation | Function | undefined | Callback that runs before tick rotation is determined. Passed a single argument, the scale instance.
+afterCalculateTickRotation | Function | undefined | Callback that runs after tick rotation is determined. Passed a single argument, the scale instance.
+beforeFit | Function | undefined | Callback that runs before the scale fits to the canvas. Passed a single argument, the scale instance.
+afterFit | Function | undefined | Callback that runs after the scale fits to the canvas. Passed a single argument, the scale instance.
+afterUpdate | Function | undefined | Callback that runs at the end of the update process. Passed a single argument, the scale instance.
**gridLines** | Array | - | Options for the grid lines that run perpendicular to the axis.
*gridLines*.display | Boolean | true |
*gridLines*.color | Color | "rgba(0, 0, 0, 0.1)" | Color of the grid lines.
```javascript
{
+ // Determines the data limits. Should set this.min and this.max to be the data max/min
+ determineDataLimits: function() {},
+
// Generate tick marks. this.chart is the chart instance. The data object can be accessed as this.chart.data
// buildTicks() should create a ticks array on the scale instance, if you intend to use any of the implementations from the base class
buildTicks: function() {},
isDatasetVisible = helpers.isDatasetVisible = function(dataset) {
return !dataset.hidden;
};
+
+ helpers.callCallback = function(fn, args, _tArg) {
+ if (fn && typeof fn.call === 'function') {
+ fn.apply(_tArg, args);
+ }
+ }
}).call(this);
// Any function defined here is inherited by all scale types.
// Any function can be extended by the scale type
- beforeUpdate: helpers.noop,
+ beforeUpdate: function() {
+ helpers.callCallback(this.options.beforeUpdate, [this]);
+ },
update: function(maxWidth, maxHeight, margins) {
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
this.beforeSetDimensions();
this.setDimensions();
this.afterSetDimensions();
+
+ // Data min/max
+ this.beforeDataLimits();
+ this.determineDataLimits();
+ this.afterDataLimits();
+
// Ticks
this.beforeBuildTicks();
this.buildTicks();
return this.minSize;
},
- afterUpdate: helpers.noop,
+ afterUpdate: function() {
+ helpers.callCallback(this.options.afterUpdate, [this]);
+ },
//
- beforeSetDimensions: helpers.noop,
+ beforeSetDimensions: function() {
+ helpers.callCallback(this.options.beforeSetDimensions, [this]);
+ },
setDimensions: function() {
// Set the unconstrained dimension before label rotation
if (this.isHorizontal()) {
this.paddingRight = 0;
this.paddingBottom = 0;
},
- afterSetDimensions: helpers.noop,
+ afterSetDimensions: function() {
+ helpers.callCallback(this.options.afterSetDimensions, [this]);
+ },
- //
+ // Data limits
+ beforeDataLimits: function() {
+ helpers.callCallback(this.options.beforeDataLimits, [this]);
+ },
+ determineDataLimits: helpers.noop,
+ afterDataLimits: function() {
+ helpers.callCallback(this.options.afterDataLimits, [this]);
+ },
- beforeBuildTicks: helpers.noop,
+ //
+ beforeBuildTicks: function() {
+ helpers.callCallback(this.options.beforeBuildTicks, [this]);
+ },
buildTicks: helpers.noop,
- afterBuildTicks: helpers.noop,
+ afterBuildTicks: function() {
+ helpers.callCallback(this.options.afterBuildTicks, [this]);
+ },
- beforeTickToLabelConversion: helpers.noop,
+ beforeTickToLabelConversion: function() {
+ helpers.callCallback(this.options.beforeTickToLabelConversion, [this]);
+ },
convertTicksToLabels: function() {
// Convert ticks to strings
this.ticks = this.ticks.map(function(numericalTick, index, ticks) {
},
this);
},
- afterTickToLabelConversion: helpers.noop,
+ afterTickToLabelConversion: function() {
+ helpers.callCallback(this.options.afterTickToLabelConversion, [this]);
+ },
//
- beforeCalculateTickRotation: helpers.noop,
+ beforeCalculateTickRotation: function() {
+ helpers.callCallback(this.options.beforeCalculateTickRotation, [this]);
+ },
calculateTickRotation: function() {
//Get the width of each grid by calculating the difference
//between x offsets between 0 and 1.
this.paddingRight = Math.max(this.paddingRight, 0);
}
},
- afterCalculateTickRotation: helpers.noop,
+ afterCalculateTickRotation: function() {
+ helpers.callCallback(this.options.afterCalculateTickRotation, [this]);
+ },
//
- beforeFit: helpers.noop,
+ beforeFit: function() {
+ helpers.callCallback(this.options.beforeFit, [this]);
+ },
fit: function() {
this.minSize = {
this.height = this.minSize.height;
},
- afterFit: helpers.noop,
+ afterFit: function() {
+ helpers.callCallback(this.options.afterFit, [this]);
+ },
// Shared Methods
isHorizontal: function() {
};
var LinearScale = Chart.Scale.extend({
- buildTicks: function() {
-
+ determineDataLimits: function() {
// First Calculate the range
this.min = null;
this.max = null;
}, this);
}
- // Then calulate the ticks
- this.ticks = [];
-
- // Figure out what the max number of ticks we can support it is based on the size of
- // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
- // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
- // the graph
-
- var maxTicks;
-
- if (this.isHorizontal()) {
- maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
- Math.ceil(this.width / 50));
- } else {
- // The factor of 2 used to scale the font size has been experimentally determined.
- maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
- Math.ceil(this.height / (2 * this.options.ticks.fontSize)));
- }
-
- // Make sure we always have at least 2 ticks
- maxTicks = Math.max(2, maxTicks);
-
- // To get a "nice" value for the tick spacing, we will use the appropriately named
- // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
- // for details.
-
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
// do nothing since that would make the chart weird. If the user really wants a weird chart
// axis, they can manually override it
this.min--;
this.max++;
}
+ },
+ buildTicks: function() {
+
+ // Then calulate the ticks
+ this.ticks = [];
+
+ // Figure out what the max number of ticks we can support it is based on the size of
+ // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
+ // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
+ // the graph
+
+ var maxTicks;
+
+ if (this.isHorizontal()) {
+ maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
+ Math.ceil(this.width / 50));
+ } else {
+ // The factor of 2 used to scale the font size has been experimentally determined.
+ maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
+ Math.ceil(this.height / (2 * this.options.ticks.fontSize)));
+ }
+
+ // Make sure we always have at least 2 ticks
+ maxTicks = Math.max(2, maxTicks);
+
+ // To get a "nice" value for the tick spacing, we will use the appropriately named
+ // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
+ // for details.
var niceRange = helpers.niceNum(this.max - this.min, false);
var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
};
var LogarithmicScale = Chart.Scale.extend({
- buildTicks: function() {
-
- // Calculate Range (we may break this out into it's own lifecycle function)
-
+ determineDataLimits: function() {
+ // Calculate Range
this.min = null;
this.max = null;
this.max = 10;
}
}
-
-
+ },
+ buildTicks: function() {
// Reset the ticks array. Later on, we will draw a grid line at these positions
// The array simply contains the numerical value of the spots where ticks will be
this.tickValues = [];
tickVal = significand * Math.pow(10, exp);
}
- /*var minExponent = Math.floor(helpers.log10(this.min));
- var maxExponent = Math.ceil(helpers.log10(this.max));
-
- for (var exponent = minExponent; exponent < maxExponent; ++exponent) {
- for (var i = 1; i < 10; ++i) {
- if (this.options.ticks.min) {
-
- } else {
- this.tickValues.push(i * Math.pow(10, exponent));
- }
- }
- }*/
-
var lastTick = this.options.ticks.max !== undefined ? this.options.ticks.max : tickVal;
this.tickValues.push(lastTick);
var minSize = helpers.min([this.height, this.width]);
this.drawingArea = (this.options.display) ? (minSize / 2) - (this.options.ticks.fontSize / 2 + this.options.ticks.backdropPaddingY) : (minSize / 2);
},
- buildTicks: function() {
+ determineDataLimits: function() {
this.min = null;
this.max = null;
this.max++;
}
- this.ticks = [];
-
- // Figure out what the max number of ticks we can support it is based on the size of
- // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
- // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
- // the graph
- var maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
- Math.ceil(this.drawingArea / (1.5 * this.options.ticks.fontSize)));
- maxTicks = Math.max(2, maxTicks); // Make sure we always have at least 2 ticks
-
- // To get a "nice" value for the tick spacing, we will use the appropriately named
- // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
- // for details.
-
// If we are forcing it to begin at 0, but 0 will already be rendered on the chart,
// do nothing since that would make the chart weird. If the user really wants a weird chart
// axis, they can manually override it
this.min = 0;
}
}
+ },
+ buildTicks: function() {
+
+
+ this.ticks = [];
+
+ // Figure out what the max number of ticks we can support it is based on the size of
+ // the axis area. For now, we say that the minimum tick spacing in pixels must be 50
+ // We also limit the maximum number of ticks to 11 which gives a nice 10 squares on
+ // the graph
+ var maxTicks = Math.min(this.options.ticks.maxTicksLimit ? this.options.ticks.maxTicksLimit : 11,
+ Math.ceil(this.drawingArea / (1.5 * this.options.ticks.fontSize)));
+ maxTicks = Math.max(2, maxTicks); // Make sure we always have at least 2 ticks
+
+ // To get a "nice" value for the tick spacing, we will use the appropriately named
+ // "nice number" algorithm. See http://stackoverflow.com/questions/8506881/nice-label-algorithm-for-charts-with-minimum-ticks
+ // for details.
var niceRange = helpers.niceNum(this.max - this.min, false);
var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
getLabelMoment: function(datasetIndex, index) {
return this.labelMoments[datasetIndex][index];
},
-
- buildLabelMoments: function() {
+ determineDataLimits: function() {
+ this.labelMoments = [];
+
// Only parse these once. If the dataset does not have data as x,y pairs, we will use
// these
var scaleLabelMoments = [];
this.firstTick = this.firstTick.clone();
this.lastTick = this.lastTick.clone();
},
-
buildTicks: function(index) {
this.ticks = [];
- this.labelMoments = [];
this.unitScale = 1; // How much we scale the unit by, ie 2 means 2x unit per step
- this.buildLabelMoments();
-
// Set unit override if applicable
if (this.options.time.unit) {
this.tickUnit = this.options.time.unit || 'day';
scale.width = 50;
scale.height = 400;
- scale.buildTicks();
+ scale.determineDataLimits();
expect(scale.min).toBe(-100);
expect(scale.max).toBe(150);
});
scale.width = 50;
scale.height = 400;
- scale.buildTicks();
+ scale.determineDataLimits();
expect(scale.min).toBe(-100);
expect(scale.max).toBe(150);
});
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-100);
expect(scale.max).toBe(80);
verticalScale.width = 50;
verticalScale.height = 400;
+ verticalScale.determineDataLimits();
verticalScale.buildTicks();
expect(verticalScale.min).toBe(0);
expect(verticalScale.max).toBe(100);
horizontalScale.width = 400;
horizontalScale.height = 50;
+ horizontalScale.determineDataLimits();
horizontalScale.buildTicks();
expect(horizontalScale.min).toBe(-20);
expect(horizontalScale.max).toBe(100);
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-150);
expect(scale.max).toBe(200);
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-150);
expect(scale.max).toBe(200);
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-1);
expect(scale.max).toBe(1);
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-10);
expect(scale.max).toBe(10);
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
expect(scale.min).toBe(-1010);
expect(scale.max).toBe(1010);
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
expect(scale.ticks).toEqual([50, 45, 40, 35, 30, 25, 20]);
config.ticks.beginAtZero = true;
+ scale.determineDataLimits();
scale.buildTicks();
expect(scale.ticks).toEqual([50, 45, 40, 35, 30, 25, 20, 15, 10, 5, 0]);
mockData.datasets[0].data = [-20, -30, -40, -50];
+ scale.determineDataLimits();
scale.buildTicks();
expect(scale.ticks).toEqual([0, -5, -10, -15, -20, -25, -30, -35, -40, -45, -50]);
config.ticks.beginAtZero = false;
+ scale.determineDataLimits();
scale.buildTicks();
expect(scale.ticks).toEqual([-20, -25, -30, -35, -40, -45, -50]);
});
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
// Counts down because the lines are drawn top to bottom
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
// Reverse mode makes this count up
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
// Counts down because the lines are drawn top to bottom
scale.width = 50;
scale.height = 400;
+ scale.determineDataLimits();
scale.buildTicks();
// Counts down because the lines are drawn top to bottom