</div>
<button id="randomizeData">Randomize Data</button>
<script>
- var randomScalingFactor = function() {
- return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
- };
- var randomColorFactor = function() {
- return Math.round(Math.random() * 255);
- };
+ var randomScalingFactor = function() {
+ return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
+ };
+ var randomColorFactor = function() {
+ return Math.round(Math.random() * 255);
+ };
- var barChartData = {
- labels: ["January", "February", "March", "April", "May", "June", "July"],
- datasets: [{
- label: 'Dataset 1',
- backgroundColor: "rgba(220,220,220,0.5)",
- data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
- }, {
- label: 'Dataset 2',
- backgroundColor: "rgba(151,187,205,0.5)",
- data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
- }, {
- label: 'Dataset 3',
- backgroundColor: "rgba(151,187,205,0.5)",
- data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
- }]
+ var barChartData = {
+ labels: ["January", "February", "March", "April", "May", "June", "July"],
+ datasets: [{
+ label: 'Dataset 1',
+ backgroundColor: "rgba(220,220,220,0.5)",
+ data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
+ }, {
+ label: 'Dataset 2',
+ backgroundColor: "rgba(151,187,205,0.5)",
+ data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
+ }, {
+ label: 'Dataset 3',
+ backgroundColor: "rgba(151,187,205,0.5)",
+ data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
+ }]
- };
- window.onload = function() {
- var ctx = document.getElementById("canvas").getContext("2d");
- window.myBar = new Chart(ctx, {
- type: 'bar',
- data: barChartData,
- options: {
- responsive: true,
- scales: {
- xAxes: [{
- stacked: true,
- }],
- yAxes: [{
- stacked: true
- }]
+ };
+ window.onload = function() {
+ var ctx = document.getElementById("canvas").getContext("2d");
+ window.myBar = new Chart(ctx, {
+ type: 'bar',
+ data: barChartData,
+ options: {
+ responsive: true,
+ scales: {
+ xAxes: [{
+ stacked: true,
+ }],
+ yAxes: [{
+ stacked: true
+ }]
+ }
}
- }
- });
- };
+ });
+ };
- $('#randomizeData').click(function() {
- $.each(barChartData.datasets, function(i, dataset) {
- dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
- dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
+ $('#randomizeData').click(function() {
+ $.each(barChartData.datasets, function(i, dataset) {
+ dataset.backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
+ dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
+ });
+ window.myBar.update();
});
- window.myBar.update();
- });
</script>
</body>
</head>
<body>
- <div id="container" style="width: 50%; height: 25%; display:none;">
+ <div id="container" style="width: 50%; height: 25%;">
<canvas id="canvas" height="450" width="600"></canvas>
</div>
<button id="randomizeData">Randomize Data</button>
data: barChartData,
options: {
responsive: true,
- scales: {
- xAxes: [{
- // So that bars fill the entire width of the grid
- categorySpacing: 10,
- spacing: 0
- }]
- }
}
});
config.type = 'bar';
return new Chart(context, config);
- }
-
+ };
+
}).call(this);
scales: {
xAxes: [{
type: "category",
- categorySpacing: 10,
- spacing: 1,
+
+ // Specific to Bar Controller
+ categoryPercentage: 0.8,
+ barPercentage: 0.9,
// grid line settings
gridLines: {
return this.chart.data.datasets[this.index];
},
- getScaleForId: function(scaleID) {
+ getScaleForID: function(scaleID) {
return this.chart.scales[scaleID];
},
// Make sure that we handle number of datapoints changing
if (numData < numRectangles) {
// Remove excess bars for data points that have been removed
- this.getDataset().metaData.splice(numData, numRectangles - numData)
+ this.getDataset().metaData.splice(numData, numRectangles - numData);
} else if (numData > numRectangles) {
// Add new elements
for (var index = numRectangles; index < numData; ++index) {
},
updateElement: function updateElement(rectangle, index, reset, numBars) {
- var xScale = this.getScaleForId(this.getDataset().xAxisID);
- var yScale = this.getScaleForId(this.getDataset().yAxisID);
+
+ var xScale = this.getScaleForID(this.getDataset().xAxisID);
+ var yScale = this.getScaleForID(this.getDataset().yAxisID);
+
var yScalePoint;
if (yScale.min < 0 && yScale.max < 0) {
// Desired view properties
_model: {
- x: xScale.calculateBarX(numBars, this.index, index),
- y: reset ? yScalePoint : yScale.calculateBarY(this.index, index),
+ x: this.calculateBarX(this.index, index),
+ y: reset ? yScalePoint : this.calculateBarY(this.index, index),
// Tooltip
label: this.chart.data.labels[index],
datasetLabel: this.getDataset().label,
// Appearance
- base: yScale.calculateBarBase(this.index, index),
- width: xScale.calculateBarWidth(numBars),
+ base: this.calculateBarBase(this.index, index),
+ width: this.calculateBarWidth(numBars),
backgroundColor: rectangle.custom && rectangle.custom.backgroundColor ? rectangle.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.rectangle.backgroundColor),
borderColor: rectangle.custom && rectangle.custom.borderColor ? rectangle.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.rectangle.borderColor),
borderWidth: rectangle.custom && rectangle.custom.borderWidth ? rectangle.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.rectangle.borderWidth),
rectangle.pivot();
},
+ calculateBarBase: function(datasetIndex, index) {
+
+ var xScale = this.getScaleForID(this.getDataset().xAxisID);
+ var yScale = this.getScaleForID(this.getDataset().yAxisID);
+
+ var base = 0;
+
+ if (yScale.options.stacked) {
+
+ var value = this.chart.data.datasets[datasetIndex].data[index];
+
+ if (value < 0) {
+ for (var i = 0; i < datasetIndex; i++) {
+ if (this.chart.data.datasets[i].yAxisID === yScale.id) {
+ base += this.chart.data.datasets[i].data[index] < 0 ? this.chart.data.datasets[i].data[index] : 0;
+ }
+ }
+ } else {
+ for (var j = 0; j < datasetIndex; j++) {
+ if (this.chart.data.datasets[j].yAxisID === yScale.id) {
+ base += this.chart.data.datasets[j].data[index] > 0 ? this.chart.data.datasets[j].data[index] : 0;
+ }
+ }
+ }
+
+ return yScale.getPixelForValue(base);
+ }
+
+ base = yScale.getPixelForValue(yScale.min);
+
+ if (yScale.beginAtZero || ((yScale.min <= 0 && yScale.max >= 0) || (yScale.min >= 0 && yScale.max <= 0))) {
+ base = yScale.getPixelForValue(0);
+ base += yScale.options.gridLines.lineWidth;
+ } else if (yScale.min < 0 && yScale.max < 0) {
+ // All values are negative. Use the top as the base
+ base = yScale.getPixelForValue(yScale.max);
+ }
+
+ return base;
+
+ },
+
+ calculateBarWidth: function() {
+
+ var xScale = this.getScaleForID(this.getDataset().xAxisID);
+ var yScale = this.getScaleForID(this.getDataset().yAxisID);
+
+ if (xScale.options.stacked) {
+ return xScale.ruler.categoryWidth;
+ }
+
+ return xScale.ruler.barWidth;
+
+ },
+
+
+ calculateBarX: function(datasetIndex, elementIndex) {
+
+ var xScale = this.getScaleForID(this.getDataset().xAxisID);
+ var yScale = this.getScaleForID(this.getDataset().yAxisID);
+
+ var leftTick = xScale.getPixelFromTickIndex(elementIndex);
+
+ if (yScale.options.stacked) {
+ return leftTick + (xScale.ruler.categoryWidth / 2) + xScale.ruler.categorySpacing;
+ }
+
+ return leftTick +
+ (xScale.ruler.barWidth / 2) +
+ xScale.ruler.categorySpacing +
+ (xScale.ruler.barWidth * datasetIndex) +
+ (xScale.ruler.barSpacing / 2) +
+ (xScale.ruler.barSpacing * datasetIndex);
+ },
+
+ calculateBarY: function(datasetIndex, index) {
+
+ var xScale = this.getScaleForID(this.getDataset().xAxisID);
+ var yScale = this.getScaleForID(this.getDataset().yAxisID);
+
+ var value = this.getDataset().data[index];
+
+ if (yScale.options.stacked) {
+
+ var sumPos = 0,
+ sumNeg = 0;
+
+ for (var i = 0; i < datasetIndex; i++) {
+ if (this.chart.data.datasets[i].data[index] < 0) {
+ sumNeg += this.chart.data.datasets[i].data[index] || 0;
+ } else {
+ sumPos += this.chart.data.datasets[i].data[index] || 0;
+ }
+ }
+
+ if (value < 0) {
+ return yScale.getPixelForValue(sumNeg + value);
+ } else {
+ return yScale.getPixelForValue(sumPos + value);
+ }
+
+ return yScale.getPixelForValue(value);
+ }
+
+ return yScale.getPixelForValue(value);
+ },
+
draw: function(ease) {
var easingDecimal = ease || 1;
helpers.each(this.getDataset().metaData, function(rectangle, index) {
// Desired view properties
_model: {
- x: xScale.getPointPixelForValue(this.getDataset().data[index], index, this.index),
- y: reset ? scaleBase : yScale.getPointPixelForValue(this.getDataset().data[index], index, this.index),
+ x: xScale.getPixelForValue(this.getDataset().data[index], index, this.index),
+ y: reset ? scaleBase : yScale.getPixelForValue(this.getDataset().data[index], index, this.index),
// Appearance
tension: point.custom && point.custom.tension ? point.custom.tension : (this.getDataset().tension || this.chart.options.elements.line.tension),
radius: point.custom && point.custom.radius ? point.custom.radius : helpers.getValueAtIndexOrDefault(this.getDataset().radius, index, this.chart.options.elements.point.radius),
return base;
},
+ extendDeep = helpers.extendDeep = function(_base) {
+ return _extendDeep.apply(this, arguments);
+
+ function _extendDeep(dst) {
+ helpers.each(arguments, function(obj) {
+ if (obj !== dst) {
+ helpers.each(obj, function(value, key) {
+ if (dst[key] && dst[key].constructor && dst[key].constructor === Object) {
+ _extendDeep(dst[key], value);
+ } else {
+ dst[key] = value;
+ }
+ });
+ }
+ });
+ return dst;
+ }
+ },
scaleMerge = helpers.scaleMerge = function(_base, extension) {
var base = clone(_base);
getMaximumWidth = helpers.getMaximumWidth = function(domNode) {
var container = domNode.parentNode;
var padding = parseInt(getStyle(container, 'padding-left')) + parseInt(getStyle(container, 'padding-right'));
-
+
var w = container.clientWidth - padding;
var cw = getConstraintWidth(domNode);
if (cw !== undefined) {
getMaximumHeight = helpers.getMaximumHeight = function(domNode) {
var container = domNode.parentNode;
var padding = parseInt(getStyle(container, 'padding-top')) + parseInt(getStyle(container, 'padding-bottom'));
-
+
var h = container.clientHeight - padding;
var ch = getConstraintHeight(domNode);
if (ch !== undefined) {
Chart = root.Chart,
helpers = Chart.helpers;
- // The scale service is used to resize charts along with all of their axes. We make this as
- // a service where scales are registered with their respective charts so that changing the
- // scales does not require
- Chart.scaleService = {
- // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
- // use the new chart options to grab the correct scale
- constructors: {},
- // Use a registration function so that we can move to an ES6 map when we no longer need to support
- // old browsers
- // Scale config defaults
- defaults: {},
- registerScaleType: function(type, scaleConstructor, defaults) {
- this.constructors[type] = scaleConstructor;
- this.defaults[type] = defaults;
+ Chart.defaults.scale = {
+ display: true,
+
+ // grid line settings
+ gridLines: {
+ show: true,
+ color: "rgba(0, 0, 0, 0.1)",
+ lineWidth: 1,
+ drawOnChartArea: true,
+ drawTicks: true,
+ zeroLineWidth: 1,
+ zeroLineColor: "rgba(0,0,0,0.25)",
+ offsetGridLines: false,
},
- getScaleConstructor: function(type) {
- return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
+
+ // label settings
+ ticks: {
+ show: true,
+ minRotation: 20,
+ maxRotation: 90,
+ template: "<%=value%>",
+ fontSize: 12,
+ fontStyle: "normal",
+ fontColor: "#666",
+ fontFamily: "Helvetica Neue",
},
- getScaleDefaults: function(type) {
- return this.defaults.hasOwnProperty(type) ? this.defaults[type] : {};
+ };
+
+ Chart.Scale = Chart.Element.extend({
+ isHorizontal: function() {
+ return this.options.position == "top" || this.options.position == "bottom";
},
- // The interesting function
- fitScalesForChart: function(chartInstance, width, height) {
- var xPadding = width > 30 ? 5 : 2;
- var yPadding = height > 30 ? 5 : 2;
-
- if (chartInstance) {
- var leftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
- return scaleInstance.options.position == "left";
- });
- var rightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
- return scaleInstance.options.position == "right";
- });
- var topScales = helpers.where(chartInstance.scales, function(scaleInstance) {
- return scaleInstance.options.position == "top";
- });
- var bottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
- return scaleInstance.options.position == "bottom";
- });
-
- // Essentially we now have any number of scales on each of the 4 sides.
- // Our canvas looks like the following.
- // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
- // B1 is the bottom axis
- // |------------------------------------------------------|
- // | | T1 | |
- // |----|-----|-------------------------------------|-----|
- // | | | | |
- // | L1 | L2 | Chart area | R1 |
- // | | | | |
- // | | | | |
- // |----|-----|-------------------------------------|-----|
- // | | B1 | |
- // | | | |
- // |------------------------------------------------------|
-
- // What we do to find the best sizing, we do the following
- // 1. Determine the minimum size of the chart area.
- // 2. Split the remaining width equally between each vertical axis
- // 3. Split the remaining height equally between each horizontal axis
- // 4. Give each scale the maximum size it can be. The scale will return it's minimum size
- // 5. Adjust the sizes of each axis based on it's minimum reported size.
- // 6. Refit each axis
- // 7. Position each axis in the final location
- // 8. Tell the chart the final location of the chart area
-
- // Step 1
- var chartWidth = width / 2; // min 50%
- var chartHeight = height / 2; // min 50%
-
- chartWidth -= (2 * xPadding);
- chartHeight -= (2 * yPadding);
-
-
- // Step 2
- var verticalScaleWidth = (width - chartWidth) / (leftScales.length + rightScales.length);
-
- // Step 3
- var horizontalScaleHeight = (height - chartHeight) / (topScales.length + bottomScales.length);
-
- // Step 4;
- var minimumScaleSizes = [];
-
- var verticalScaleMinSizeFunction = function(scaleInstance) {
- var minSize = scaleInstance.fit(verticalScaleWidth, chartHeight);
- minimumScaleSizes.push({
- horizontal: false,
- minSize: minSize,
- scale: scaleInstance,
- });
- };
-
- var horizontalScaleMinSizeFunction = function(scaleInstance) {
- var minSize = scaleInstance.fit(chartWidth, horizontalScaleHeight);
- minimumScaleSizes.push({
- horizontal: true,
- minSize: minSize,
- scale: scaleInstance,
- });
- };
-
- // vertical scales
- helpers.each(leftScales, verticalScaleMinSizeFunction);
- helpers.each(rightScales, verticalScaleMinSizeFunction);
-
- // horizontal scales
- helpers.each(topScales, horizontalScaleMinSizeFunction);
- helpers.each(bottomScales, horizontalScaleMinSizeFunction);
-
- // Step 5
- var maxChartHeight = height - (2 * yPadding);
- var maxChartWidth = width - (2 * xPadding);
-
- helpers.each(minimumScaleSizes, function(wrapper) {
- if (wrapper.horizontal) {
- maxChartHeight -= wrapper.minSize.height;
- } else {
- maxChartWidth -= wrapper.minSize.width;
- }
- });
+ calculateTickRotation: function(maxHeight, margins) {
+ //Get the width of each grid by calculating the difference
+ //between x offsets between 0 and 1.
+ var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
+ this.ctx.font = labelFont;
+
+ var firstWidth = this.ctx.measureText(this.ticks[0]).width;
+ var lastWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
+ var firstRotated;
+ var lastRotated;
+
+ this.paddingRight = lastWidth / 2 + 3;
+ this.paddingLeft = firstWidth / 2 + 3;
+
+ this.labelRotation = 0;
- // At this point, maxChartHeight and maxChartWidth are the size the chart area could
- // be if the axes are drawn at their minimum sizes.
+ if (this.options.display) {
+ var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
+ var cosRotation;
+ var sinRotation;
+ var firstRotatedWidth;
- // Step 6
- var verticalScaleFitFunction = function(scaleInstance) {
- var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
- return wrapper.scale === scaleInstance;
- });
+ this.labelWidth = originalLabelWidth;
- if (wrapper) {
- scaleInstance.fit(wrapper.minSize.width, maxChartHeight);
+ //Allow 3 pixels x2 padding either side for label readability
+ // only the index matters for a dataset scale, but we want a consistent interface between scales
+
+ var tickWidth = this.ruler.tick - 6;
+
+ //Max label rotation can be set or default to 90 - also act as a loop counter
+ while (this.labelWidth > tickWidth && this.labelRotation <= this.options.ticks.maxRotation) {
+ cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
+ sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
+
+ firstRotated = cosRotation * firstWidth;
+ lastRotated = cosRotation * lastWidth;
+
+ // We're right aligning the text now.
+ if (firstRotated + this.options.ticks.fontSize / 2 > this.yLabelWidth) {
+ this.paddingLeft = firstRotated + this.options.ticks.fontSize / 2;
}
- };
-
- var horizontalScaleFitFunction = function(scaleInstance) {
- var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
- return wrapper.scale === scaleInstance;
- });
-
- var scaleMargin = {
- left: totalLeftWidth,
- right: totalRightWidth,
- top: 0,
- bottom: 0,
- };
-
- if (wrapper) {
- scaleInstance.fit(maxChartWidth, wrapper.minSize.height, scaleMargin);
+
+ this.paddingRight = this.options.ticks.fontSize / 2;
+
+ if (sinRotation * originalLabelWidth > maxHeight) {
+ // go back one step
+ this.labelRotation--;
+ break;
}
- };
-
- var totalLeftWidth = xPadding;
- var totalRightWidth = xPadding;
- var totalTopHeight = yPadding;
- var totalBottomHeight = yPadding;
-
- helpers.each(leftScales, verticalScaleFitFunction);
- helpers.each(rightScales, verticalScaleFitFunction);
-
- // Figure out how much margin is on the left and right of the horizontal axes
- helpers.each(leftScales, function(scaleInstance) {
- totalLeftWidth += scaleInstance.width;
- });
-
- helpers.each(rightScales, function(scaleInstance) {
- totalRightWidth += scaleInstance.width;
- });
-
- helpers.each(topScales, horizontalScaleFitFunction);
- helpers.each(bottomScales, horizontalScaleFitFunction);
-
- helpers.each(topScales, function(scaleInstance) {
- totalTopHeight += scaleInstance.height;
- });
- helpers.each(bottomScales, function(scaleInstance) {
- totalBottomHeight += scaleInstance.height;
- });
-
- // Let the left scale know the final margin
- helpers.each(leftScales, function(scaleInstance) {
- var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
- return wrapper.scale === scaleInstance;
- });
-
- var scaleMargin = {
- left: 0,
- right: 0,
- top: totalTopHeight,
- bottom: totalBottomHeight
- };
-
- if (wrapper) {
- scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
+
+ this.labelRotation++;
+ this.labelRotation = Math.max(this.labelRotation, this.options.ticks.minRotation);
+ this.labelWidth = cosRotation * originalLabelWidth;
+
+ }
+ } else {
+ this.labelWidth = 0;
+ this.paddingRight = 0;
+ this.paddingLeft = 0;
+ }
+
+ if (margins) {
+ this.paddingLeft -= margins.left;
+ this.paddingRight -= margins.right;
+
+ this.paddingLeft = Math.max(this.paddingLeft, 0);
+ this.paddingRight = Math.max(this.paddingRight, 0);
+ }
+
+ },
+ getPixelForValue: function(value, index, datasetIndex, includeOffset) {
+ // This must be called after fit has been run so that
+ // this.left, this.top, this.right, and this.bottom have been defined
+ if (this.isHorizontal()) {
+ var isRotated = (this.labelRotation > 0);
+ var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
+ var valueWidth = innerWidth / Math.max((this.data.labels.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
+ var valueOffset = (valueWidth * index) + this.paddingLeft;
+
+ if (this.options.gridLines.offsetGridLines && includeOffset) {
+ valueOffset += (valueWidth / 2);
+ }
+
+ return this.left + Math.round(valueOffset);
+ } else {
+ return this.top + (index * (this.height / this.labels.length));
+ }
+ },
+ getPixelFromTickIndex: function(index, includeOffset) {
+ // This must be called after fit has been run so that
+ // this.left, this.top, this.right, and this.bottom have been defined
+ if (this.isHorizontal()) {
+ var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
+ var tickWidth = innerWidth / Math.max((this.ticks.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
+ var pixel = (tickWidth * index) + this.paddingLeft;
+
+ if (includeOffset) {
+ pixel += tickWidth / 2;
+ }
+ return this.left + Math.round(pixel);
+ } else {
+ return this.top + (index * (this.height / this.ticks.length));
+ }
+ },
+ getPixelFromDecimal: function(decimal, includeOffset) {
+ // This must be called after fit has been run so that
+ // this.left, this.top, this.right, and this.bottom have been defined
+ if (this.isHorizontal()) {
+ var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
+ var valueOffset = (innerWidth * decimal) + this.paddingLeft;
+
+ return this.left + Math.round(valueOffset);
+ } else {
+ return this.top + (decimal * (this.height / this.ticks.length));
+ }
+ },
+ // Fit this axis to the given size
+ // @param {number} maxWidth : the max width the axis can be
+ // @param {number} maxHeight: the max height the axis can be
+ // @return {object} minSize : the minimum size needed to draw the axis
+ fit: function(maxWidth, maxHeight, margins) {
+ // Set the unconstrained dimension before label rotation
+ if (this.isHorizontal()) {
+ this.width = maxWidth;
+ } else {
+ this.height = maxHeight;
+ }
+
+ this.buildTicks();
+ this.buildRuler();
+ this.calculateTickRotation(maxHeight, margins);
+
+ var minSize = {
+ width: 0,
+ height: 0,
+ };
+
+ var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
+ var longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
+
+ // Width
+ if (this.isHorizontal()) {
+ minSize.width = maxWidth;
+ } else if (this.options.display) {
+ var labelWidth = this.options.ticks.show ? longestLabelWidth + 6 : 0;
+ minSize.width = Math.min(labelWidth, maxWidth);
+ }
+
+ // Height
+ if (this.isHorizontal() && this.options.display) {
+ var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.ticks.fontSize;
+ minSize.height = Math.min(this.options.ticks.show ? labelHeight : 0, maxHeight);
+ } else if (this.options.display) {
+ minSize.height = maxHeight;
+ }
+
+
+ this.width = minSize.width;
+ this.height = minSize.height;
+ return minSize;
+ },
+ // Actualy draw the scale on the canvas
+ // @param {rectangle} chartArea : the area of the chart to draw full grid lines on
+ draw: function(chartArea) {
+ if (this.options.display) {
+
+ var setContextLineSettings;
+
+ // Make sure we draw text in the correct color
+ this.ctx.fillStyle = this.options.ticks.fontColor;
+
+ if (this.isHorizontal()) {
+ setContextLineSettings = true;
+ var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 10;
+ var yTickEnd = this.options.position == "bottom" ? this.top + 10 : this.bottom;
+ var isRotated = this.labelRotation !== 0;
+ var skipRatio = false;
+
+ if ((this.options.ticks.fontSize + 4) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) {
+ skipRatio = 1 + Math.floor(((this.options.ticks.fontSize + 4) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight)));
}
- });
-
- helpers.each(rightScales, function(scaleInstance) {
- var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
- return wrapper.scale === scaleInstance;
- });
-
- var scaleMargin = {
- left: 0,
- right: 0,
- top: totalTopHeight,
- bottom: totalBottomHeight
- };
-
- if (wrapper) {
- scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
+
+ helpers.each(this.ticks, function(label, index) {
+ // Blank ticks
+ if ((skipRatio > 1 && index % skipRatio > 0) || (label === undefined || label === null)) {
+ return;
+ }
+ var xLineValue = this.getPixelFromTickIndex(index); // xvalues for grid lines
+ var xLabelValue = this.getPixelFromTickIndex(index, this.options.gridLines.offsetGridLines); // x values for ticks (need to consider offsetLabel option)
+
+ if (this.options.gridLines.show) {
+ if (index === 0) {
+ // Draw the first index specially
+ this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
+ this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
+ setContextLineSettings = true; // reset next time
+ } else if (setContextLineSettings) {
+ this.ctx.lineWidth = this.options.gridLines.lineWidth;
+ this.ctx.strokeStyle = this.options.gridLines.color;
+ setContextLineSettings = false;
+ }
+
+ xLineValue += helpers.aliasPixel(this.ctx.lineWidth);
+
+ // Draw the label area
+ this.ctx.beginPath();
+
+ if (this.options.gridLines.drawTicks) {
+ this.ctx.moveTo(xLineValue, yTickStart);
+ this.ctx.lineTo(xLineValue, yTickEnd);
+ }
+
+ // Draw the chart area
+ if (this.options.gridLines.drawOnChartArea) {
+ this.ctx.moveTo(xLineValue, chartArea.top);
+ this.ctx.lineTo(xLineValue, chartArea.bottom);
+ }
+
+ // Need to stroke in the loop because we are potentially changing line widths & colours
+ this.ctx.stroke();
+ }
+
+ if (this.options.ticks.show) {
+ this.ctx.save();
+ this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.top + 8);
+ this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
+ this.ctx.font = this.font;
+ this.ctx.textAlign = (isRotated) ? "right" : "center";
+ this.ctx.textBaseline = (isRotated) ? "middle" : "top";
+ this.ctx.fillText(label, 0, 0);
+ this.ctx.restore();
+ }
+ }, this);
+ } else {
+ // TODO Vertical
+ if (this.options.gridLines.show) {}
+
+ if (this.options.ticks.show) {
+ // Draw the ticks
}
- });
-
- // Recalculate because the size of each scale might have changed slightly due to the margins (label rotation for instance)
- totalLeftWidth = xPadding;
- totalRightWidth = xPadding;
- totalTopHeight = yPadding;
- totalBottomHeight = yPadding;
-
- helpers.each(leftScales, function(scaleInstance) {
- totalLeftWidth += scaleInstance.width;
- });
-
- helpers.each(rightScales, function(scaleInstance) {
- totalRightWidth += scaleInstance.width;
- });
-
- helpers.each(topScales, function(scaleInstance) {
- totalTopHeight += scaleInstance.height;
- });
- helpers.each(bottomScales, function(scaleInstance) {
- totalBottomHeight += scaleInstance.height;
- });
-
- // Figure out if our chart area changed. This would occur if the dataset scale label rotation
- // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
- // without calling `fit` again
- var newMaxChartHeight = height - totalTopHeight - totalBottomHeight;
- var newMaxChartWidth = width - totalLeftWidth - totalRightWidth;
-
- if (newMaxChartWidth !== maxChartWidth || newMaxChartHeight !== maxChartHeight) {
- helpers.each(leftScales, function(scale) {
- scale.height = newMaxChartHeight;
- });
-
- helpers.each(rightScales, function(scale) {
- scale.height = newMaxChartHeight;
- });
-
- helpers.each(topScales, function(scale) {
- scale.width = newMaxChartWidth;
- });
-
- helpers.each(bottomScales, function(scale) {
- scale.width = newMaxChartWidth;
- });
-
- maxChartHeight = newMaxChartHeight;
- maxChartWidth = newMaxChartWidth;
}
-
- // Step 7
- // Position the scales
- var left = xPadding;
- var top = yPadding;
- var right = 0;
- var bottom = 0;
-
- var verticalScalePlacer = function(scaleInstance) {
- scaleInstance.left = left;
- scaleInstance.right = left + scaleInstance.width;
- scaleInstance.top = totalTopHeight;
- scaleInstance.bottom = totalTopHeight + maxChartHeight;
-
- // Move to next point
- left = scaleInstance.right;
- };
-
- var horizontalScalePlacer = function(scaleInstance) {
- scaleInstance.left = totalLeftWidth;
- scaleInstance.right = totalLeftWidth + maxChartWidth;
- scaleInstance.top = top;
- scaleInstance.bottom = top + scaleInstance.height;
-
- // Move to next point
- top = scaleInstance.bottom;
- };
-
- helpers.each(leftScales, verticalScalePlacer);
- helpers.each(topScales, horizontalScalePlacer);
-
- // Account for chart width and height
- left += maxChartWidth;
- top += maxChartHeight;
-
- helpers.each(rightScales, verticalScalePlacer);
- helpers.each(bottomScales, horizontalScalePlacer);
-
- // Step 8
- chartInstance.chartArea = {
- left: totalLeftWidth,
- top: totalTopHeight,
- right: totalLeftWidth + maxChartWidth,
- bottom: totalTopHeight + maxChartHeight,
- };
}
}
- };
+ });
+
}).call(this);
--- /dev/null
+(function() {
+ "use strict";
+
+ var root = this,
+ Chart = root.Chart,
+ helpers = Chart.helpers;
+
+ // The scale service is used to resize charts along with all of their axes. We make this as
+ // a service where scales are registered with their respective charts so that changing the
+ // scales does not require
+ Chart.scaleService = {
+ // Scale registration object. Extensions can register new scale types (such as log or DB scales) and then
+ // use the new chart options to grab the correct scale
+ constructors: {},
+ // Use a registration function so that we can move to an ES6 map when we no longer need to support
+ // old browsers
+ // Scale config defaults
+ defaults: {},
+ registerScaleType: function(type, scaleConstructor, defaults) {
+ this.constructors[type] = scaleConstructor;
+ this.defaults[type] = helpers.extendDeep({}, Chart.defaults.scale, defaults);
+ },
+ getScaleConstructor: function(type) {
+ return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
+ },
+ getScaleDefaults: function(type) {
+ return this.defaults.hasOwnProperty(type) ? this.defaults[type] : {};
+ },
+ // The interesting function
+ fitScalesForChart: function(chartInstance, width, height) {
+ var xPadding = width > 30 ? 5 : 2;
+ var yPadding = height > 30 ? 5 : 2;
+
+ if (chartInstance) {
+ var leftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
+ return scaleInstance.options.position == "left";
+ });
+ var rightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
+ return scaleInstance.options.position == "right";
+ });
+ var topScales = helpers.where(chartInstance.scales, function(scaleInstance) {
+ return scaleInstance.options.position == "top";
+ });
+ var bottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
+ return scaleInstance.options.position == "bottom";
+ });
+
+ // Essentially we now have any number of scales on each of the 4 sides.
+ // Our canvas looks like the following.
+ // The areas L1 and L2 are the left axes. R1 is the right axis, T1 is the top axis and
+ // B1 is the bottom axis
+ // |------------------------------------------------------|
+ // | | T1 | |
+ // |----|-----|-------------------------------------|-----|
+ // | | | | |
+ // | L1 | L2 | Chart area | R1 |
+ // | | | | |
+ // | | | | |
+ // |----|-----|-------------------------------------|-----|
+ // | | B1 | |
+ // | | | |
+ // |------------------------------------------------------|
+
+ // What we do to find the best sizing, we do the following
+ // 1. Determine the minimum size of the chart area.
+ // 2. Split the remaining width equally between each vertical axis
+ // 3. Split the remaining height equally between each horizontal axis
+ // 4. Give each scale the maximum size it can be. The scale will return it's minimum size
+ // 5. Adjust the sizes of each axis based on it's minimum reported size.
+ // 6. Refit each axis
+ // 7. Position each axis in the final location
+ // 8. Tell the chart the final location of the chart area
+
+ // Step 1
+ var chartWidth = width / 2; // min 50%
+ var chartHeight = height / 2; // min 50%
+
+ chartWidth -= (2 * xPadding);
+ chartHeight -= (2 * yPadding);
+
+
+ // Step 2
+ var verticalScaleWidth = (width - chartWidth) / (leftScales.length + rightScales.length);
+
+ // Step 3
+ var horizontalScaleHeight = (height - chartHeight) / (topScales.length + bottomScales.length);
+
+ // Step 4;
+ var minimumScaleSizes = [];
+
+ var verticalScaleMinSizeFunction = function(scaleInstance) {
+ var minSize = scaleInstance.fit(verticalScaleWidth, chartHeight);
+ minimumScaleSizes.push({
+ horizontal: false,
+ minSize: minSize,
+ scale: scaleInstance,
+ });
+ };
+
+ var horizontalScaleMinSizeFunction = function(scaleInstance) {
+ var minSize = scaleInstance.fit(chartWidth, horizontalScaleHeight);
+ minimumScaleSizes.push({
+ horizontal: true,
+ minSize: minSize,
+ scale: scaleInstance,
+ });
+ };
+
+ // vertical scales
+ helpers.each(leftScales, verticalScaleMinSizeFunction);
+ helpers.each(rightScales, verticalScaleMinSizeFunction);
+
+ // horizontal scales
+ helpers.each(topScales, horizontalScaleMinSizeFunction);
+ helpers.each(bottomScales, horizontalScaleMinSizeFunction);
+
+ // Step 5
+ var maxChartHeight = height - (2 * yPadding);
+ var maxChartWidth = width - (2 * xPadding);
+
+ helpers.each(minimumScaleSizes, function(wrapper) {
+ if (wrapper.horizontal) {
+ maxChartHeight -= wrapper.minSize.height;
+ } else {
+ maxChartWidth -= wrapper.minSize.width;
+ }
+ });
+
+ // At this point, maxChartHeight and maxChartWidth are the size the chart area could
+ // be if the axes are drawn at their minimum sizes.
+
+ // Step 6
+ var verticalScaleFitFunction = function(scaleInstance) {
+ var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
+ return wrapper.scale === scaleInstance;
+ });
+
+ if (wrapper) {
+ scaleInstance.fit(wrapper.minSize.width, maxChartHeight);
+ }
+ };
+
+ var horizontalScaleFitFunction = function(scaleInstance) {
+ var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
+ return wrapper.scale === scaleInstance;
+ });
+
+ var scaleMargin = {
+ left: totalLeftWidth,
+ right: totalRightWidth,
+ top: 0,
+ bottom: 0,
+ };
+
+ if (wrapper) {
+ scaleInstance.fit(maxChartWidth, wrapper.minSize.height, scaleMargin);
+ }
+ };
+
+ var totalLeftWidth = xPadding;
+ var totalRightWidth = xPadding;
+ var totalTopHeight = yPadding;
+ var totalBottomHeight = yPadding;
+
+ helpers.each(leftScales, verticalScaleFitFunction);
+ helpers.each(rightScales, verticalScaleFitFunction);
+
+ // Figure out how much margin is on the left and right of the horizontal axes
+ helpers.each(leftScales, function(scaleInstance) {
+ totalLeftWidth += scaleInstance.width;
+ });
+
+ helpers.each(rightScales, function(scaleInstance) {
+ totalRightWidth += scaleInstance.width;
+ });
+
+ helpers.each(topScales, horizontalScaleFitFunction);
+ helpers.each(bottomScales, horizontalScaleFitFunction);
+
+ helpers.each(topScales, function(scaleInstance) {
+ totalTopHeight += scaleInstance.height;
+ });
+ helpers.each(bottomScales, function(scaleInstance) {
+ totalBottomHeight += scaleInstance.height;
+ });
+
+ // Let the left scale know the final margin
+ helpers.each(leftScales, function(scaleInstance) {
+ var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
+ return wrapper.scale === scaleInstance;
+ });
+
+ var scaleMargin = {
+ left: 0,
+ right: 0,
+ top: totalTopHeight,
+ bottom: totalBottomHeight
+ };
+
+ if (wrapper) {
+ scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
+ }
+ });
+
+ helpers.each(rightScales, function(scaleInstance) {
+ var wrapper = helpers.findNextWhere(minimumScaleSizes, function(wrapper) {
+ return wrapper.scale === scaleInstance;
+ });
+
+ var scaleMargin = {
+ left: 0,
+ right: 0,
+ top: totalTopHeight,
+ bottom: totalBottomHeight
+ };
+
+ if (wrapper) {
+ scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
+ }
+ });
+
+ // Recalculate because the size of each scale might have changed slightly due to the margins (label rotation for instance)
+ totalLeftWidth = xPadding;
+ totalRightWidth = xPadding;
+ totalTopHeight = yPadding;
+ totalBottomHeight = yPadding;
+
+ helpers.each(leftScales, function(scaleInstance) {
+ totalLeftWidth += scaleInstance.width;
+ });
+
+ helpers.each(rightScales, function(scaleInstance) {
+ totalRightWidth += scaleInstance.width;
+ });
+
+ helpers.each(topScales, function(scaleInstance) {
+ totalTopHeight += scaleInstance.height;
+ });
+ helpers.each(bottomScales, function(scaleInstance) {
+ totalBottomHeight += scaleInstance.height;
+ });
+
+ // Figure out if our chart area changed. This would occur if the dataset scale label rotation
+ // changed due to the application of the margins in step 6. Since we can only get bigger, this is safe to do
+ // without calling `fit` again
+ var newMaxChartHeight = height - totalTopHeight - totalBottomHeight;
+ var newMaxChartWidth = width - totalLeftWidth - totalRightWidth;
+
+ if (newMaxChartWidth !== maxChartWidth || newMaxChartHeight !== maxChartHeight) {
+ helpers.each(leftScales, function(scale) {
+ scale.height = newMaxChartHeight;
+ });
+
+ helpers.each(rightScales, function(scale) {
+ scale.height = newMaxChartHeight;
+ });
+
+ helpers.each(topScales, function(scale) {
+ scale.width = newMaxChartWidth;
+ });
+
+ helpers.each(bottomScales, function(scale) {
+ scale.width = newMaxChartWidth;
+ });
+
+ maxChartHeight = newMaxChartHeight;
+ maxChartWidth = newMaxChartWidth;
+ }
+
+ // Step 7
+ // Position the scales
+ var left = xPadding;
+ var top = yPadding;
+ var right = 0;
+ var bottom = 0;
+
+ var verticalScalePlacer = function(scaleInstance) {
+ scaleInstance.left = left;
+ scaleInstance.right = left + scaleInstance.width;
+ scaleInstance.top = totalTopHeight;
+ scaleInstance.bottom = totalTopHeight + maxChartHeight;
+
+ // Move to next point
+ left = scaleInstance.right;
+ };
+
+ var horizontalScalePlacer = function(scaleInstance) {
+ scaleInstance.left = totalLeftWidth;
+ scaleInstance.right = totalLeftWidth + maxChartWidth;
+ scaleInstance.top = top;
+ scaleInstance.bottom = top + scaleInstance.height;
+
+ // Move to next point
+ top = scaleInstance.bottom;
+ };
+
+ helpers.each(leftScales, verticalScalePlacer);
+ helpers.each(topScales, horizontalScalePlacer);
+
+ // Account for chart width and height
+ left += maxChartWidth;
+ top += maxChartHeight;
+
+ helpers.each(rightScales, verticalScalePlacer);
+ helpers.each(bottomScales, horizontalScalePlacer);
+
+ // Step 8
+ chartInstance.chartArea = {
+ left: totalLeftWidth,
+ top: totalTopHeight,
+ right: totalLeftWidth + maxChartWidth,
+ bottom: totalTopHeight + maxChartHeight,
+ };
+ }
+ }
+ };
+
+
+}).call(this);
// Default config for a category scale
var defaultConfig = {
- display: true,
position: "bottom",
-
- // grid line settings
- gridLines: {
- show: true,
- color: "rgba(0, 0, 0, 0.1)",
- lineWidth: 1,
- drawOnChartArea: true,
- drawTicks: true,
- zeroLineWidth: 1,
- zeroLineColor: "rgba(0,0,0,0.25)",
- offsetGridLines: false,
- },
-
- // scale label
- scaleLabel: {
- fontColor: '#666',
- fontFamily: 'Helvetica Neue',
- fontSize: 12,
- fontStyle: 'normal',
-
- // actual label
- labelString: '',
-
- // display property
- show: false,
- },
-
- // label settings
- labels: {
- show: true,
- maxRotation: 90,
- template: "<%=value%>",
- fontSize: 12,
- fontStyle: "normal",
- fontColor: "#666",
- fontFamily: "Helvetica Neue",
- },
};
- var DatasetScale = Chart.Element.extend({
- isHorizontal: function() {
- return this.options.position == "top" || this.options.position == "bottom";
- },
- buildLabels: function(index) {
- this.labels = [];
+ var DatasetScale = Chart.Scale.extend({
+ buildTicks: function(index) {
+ this.ticks = [];
- if (this.options.labels.userCallback) {
+ if (this.options.ticks.userCallback) {
this.data.labels.forEach(function(labelString, index) {
- this.labels.push(this.options.labels.userCallback(labelString, index));
+ this.ticks.push(this.options.ticks.userCallback(labelString, index));
}, this);
} else {
- this.labels = this.data.labels;
- }
- },
- getPixelForValue: function(value, index, datasetIndex, includeOffset) {
- // This must be called after fit has been run so that
- // this.left, this.top, this.right, and this.bottom have been defined
- if (this.isHorizontal()) {
- var isRotated = (this.labelRotation > 0);
- var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
- var valueWidth = innerWidth / Math.max((this.labels.length - ((this.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
- var valueOffset = (valueWidth * index) + this.paddingLeft;
-
- if (this.options.gridLines.offsetGridLines && includeOffset) {
- valueOffset += (valueWidth / 2);
- }
-
- return this.left + Math.round(valueOffset);
- } else {
- return this.top + (index * (this.height / this.labels.length));
- }
- },
- getPointPixelForValue: function(value, index, datasetIndex) {
- return this.getPixelForValue(value, index, datasetIndex, true);
- },
-
- // Functions needed for bar charts
- calculateBaseWidth: function() {
- var base = this.getPixelForValue(null, 1, 0, true) - this.getPixelForValue(null, 0, 0, true);
- var spacing = 2 * this.options.categorySpacing;
- if (base < spacing * 2) {
- var mod = Math.min((spacing * 2) / base, 1.5);
- base = (base / 2) * mod;
- return base;
- }
- return base - spacing;
- },
- calculateBarWidth: function(barDatasetCount) {
- //The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
- var baseWidth = Math.max(this.calculateBaseWidth() - (this.options.stacked ? 0 : (barDatasetCount - 1) * this.options.spacing), 1);
-
- if (this.options.stacked) {
- return Math.max(baseWidth, 1);
- }
- return Math.max((baseWidth / barDatasetCount), 1);
- },
- calculateBarX: function(barDatasetCount, datasetIndex, elementIndex) {
- var xWidth = this.calculateBaseWidth(),
- xAbsolute = this.getPixelForValue(null, elementIndex, datasetIndex, true) - (xWidth / 2),
- barWidth = this.calculateBarWidth(barDatasetCount);
-
- if (this.options.stacked) {
- return xAbsolute + barWidth / 2;
- }
-
- return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * this.options.spacing) + barWidth / 2;
- },
-
- calculateLabelRotation: function(maxHeight, margins) {
- //Get the width of each grid by calculating the difference
- //between x offsets between 0 and 1.
- var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
- this.ctx.font = labelFont;
-
- var firstWidth = this.ctx.measureText(this.labels[0]).width;
- var lastWidth = this.ctx.measureText(this.labels[this.labels.length - 1]).width;
- var firstRotated;
- var lastRotated;
-
- this.paddingRight = lastWidth / 2 + 3;
- this.paddingLeft = firstWidth / 2 + 3;
-
- this.labelRotation = 0;
-
- if (this.options.display) {
- var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.labels);
- var cosRotation;
- var sinRotation;
- var firstRotatedWidth;
-
- this.labelWidth = originalLabelWidth;
-
- //Allow 3 pixels x2 padding either side for label readability
- // only the index matters for a dataset scale, but we want a consistent interface between scales
-
- var datasetWidth = Math.floor(this.getPixelForValue(0, 1) - this.getPixelForValue(0, 0)) - 6;
-
- //Max label rotation can be set or default to 90 - also act as a loop counter
- while (this.labelWidth > datasetWidth && this.labelRotation <= this.options.labels.maxRotation) {
- cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
- sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
-
- firstRotated = cosRotation * firstWidth;
- lastRotated = cosRotation * lastWidth;
-
- // We're right aligning the text now.
- if (firstRotated + this.options.labels.fontSize / 2 > this.yLabelWidth) {
- this.paddingLeft = firstRotated + this.options.labels.fontSize / 2;
- }
-
- this.paddingRight = this.options.labels.fontSize / 2;
-
- if (sinRotation * originalLabelWidth > maxHeight) {
- // go back one step
- this.labelRotation--;
- break;
- }
-
- this.labelRotation++;
- this.labelWidth = cosRotation * originalLabelWidth;
-
- }
- } else {
- this.labelWidth = 0;
- this.paddingRight = 0;
- this.paddingLeft = 0;
+ this.ticks = this.data.labels;
}
-
- if (margins) {
- this.paddingLeft -= margins.left;
- this.paddingRight -= margins.right;
-
- this.paddingLeft = Math.max(this.paddingLeft, 0);
- this.paddingRight = Math.max(this.paddingRight, 0);
- }
-
},
- // Fit this axis to the given size
- // @param {number} maxWidth : the max width the axis can be
- // @param {number} maxHeight: the max height the axis can be
- // @return {object} minSize : the minimum size needed to draw the axis
- fit: function(maxWidth, maxHeight, margins) {
- // Set the unconstrained dimension before label rotation
- if (this.isHorizontal()) {
- this.width = maxWidth;
- } else {
- this.height = maxHeight;
- }
-
- this.buildLabels();
- this.calculateLabelRotation(maxHeight, margins);
-
- var minSize = {
- width: 0,
- height: 0,
- };
-
- // Are we showing a label for the scale?
- if (this.options.scaleLabel.show) {
- if (this.isHorizontal()) {
- minSize.height += (this.options.scaleLabel.fontSize * 1.5);
- } else {
- minSize.width += (this.options.scaleLabel.fontSize * 1.5);
- }
- }
-
- var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
- var longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.labels);
-
- // Width
- if (this.isHorizontal()) {
- minSize.width = maxWidth;
- } else if (this.options.display) {
- var labelWidth = this.options.labels.show ? longestLabelWidth + 6 : 0;
- minSize.width += labelWidth;
- minSize.width = Math.min(minSize.width, maxWidth);
- }
-
- // Height
- if (this.isHorizontal() && this.options.display) {
- var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.labels.fontSize;
- minSize.height += this.options.labels.show ? labelHeight : 0;
- minSize.height = Math.min(minSize.height, maxHeight);
- } else if (this.options.display) {
- minSize.height = maxHeight;
- }
+ buildRuler: function() {
+ var datasetCount = this.data.datasets.length;
- this.width = minSize.width;
- this.height = minSize.height;
- return minSize;
+ this.ruler = {};
+ this.ruler.tickWidth = this.getPixelFromTickIndex(1) - this.getPixelFromTickIndex(0) + 3; // TODO: Why is 2 needed here to make it take the full width?
+ this.ruler.categoryWidth = this.ruler.tickWidth * this.options.categoryPercentage;
+ this.ruler.categorySpacing = (this.ruler.tickWidth - (this.ruler.tickWidth * this.options.categoryPercentage)) / 2;
+ this.ruler.allBarsWidth = ((this.ruler.tickWidth - (this.ruler.categorySpacing * 2)) / datasetCount);
+ this.ruler.barWidth = this.ruler.allBarsWidth * this.options.barPercentage;
+ this.ruler.barSpacing = this.ruler.allBarsWidth - (this.ruler.allBarsWidth * this.options.barPercentage);
},
- // Actualy draw the scale on the canvas
- // @param {rectangle} chartArea : the area of the chart to draw full grid lines on
- draw: function(chartArea) {
- if (this.options.display) {
-
- var setContextLineSettings;
-
- // Make sure we draw text in the correct color
- this.ctx.fillStyle = this.options.labels.fontColor;
-
- if (this.isHorizontal()) {
- setContextLineSettings = true;
- var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 10;
- var yTickEnd = this.options.position == "bottom" ? this.top + 10 : this.bottom;
- var isRotated = this.labelRotation !== 0;
- var skipRatio = false;
-
- if ((this.options.labels.fontSize + 4) * this.labels.length > (this.width - (this.paddingLeft + this.paddingRight))) {
- skipRatio = 1 + Math.floor(((this.options.labels.fontSize + 4) * this.labels.length) / (this.width - (this.paddingLeft + this.paddingRight)));
- }
-
- helpers.each(this.labels, function(label, index) {
- // Blank labels
- if ((skipRatio > 1 && index % skipRatio > 0) || (label === undefined || label === null)) {
- return;
- }
- var xLineValue = this.getPixelForValue(label, index, null, false); // xvalues for grid lines
- var xLabelValue = this.getPixelForValue(label, index, null, true); // x values for labels (need to consider offsetLabel option)
-
- if (this.options.gridLines.show) {
- if (index === 0) {
- // Draw the first index specially
- this.ctx.lineWidth = this.options.gridLines.zeroLineWidth;
- this.ctx.strokeStyle = this.options.gridLines.zeroLineColor;
- setContextLineSettings = true; // reset next time
- } else if (setContextLineSettings) {
- this.ctx.lineWidth = this.options.gridLines.lineWidth;
- this.ctx.strokeStyle = this.options.gridLines.color;
- setContextLineSettings = false;
- }
-
- xLineValue += helpers.aliasPixel(this.ctx.lineWidth);
-
- // Draw the label area
- this.ctx.beginPath();
-
- if (this.options.gridLines.drawTicks) {
- this.ctx.moveTo(xLineValue, yTickStart);
- this.ctx.lineTo(xLineValue, yTickEnd);
- }
- // Draw the chart area
- if (this.options.gridLines.drawOnChartArea) {
- this.ctx.moveTo(xLineValue, chartArea.top);
- this.ctx.lineTo(xLineValue, chartArea.bottom);
- }
-
- // Need to stroke in the loop because we are potentially changing line widths & colours
- this.ctx.stroke();
- }
-
- if (this.options.labels.show) {
- this.ctx.save();
- this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.top + 8);
- this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
- this.ctx.font = this.font;
- this.ctx.textAlign = (isRotated) ? "right" : "center";
- this.ctx.textBaseline = (isRotated) ? "middle" : "top";
- this.ctx.fillText(label, 0, 0);
- this.ctx.restore();
- }
- }, this);
-
- if (this.options.scaleLabel.show) {
- // Draw the scale label
- this.ctx.textAlign = "center";
- this.ctx.textBaseline = 'middle';
- this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
-
- var scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width
- var scaleLabelY = this.options.position == 'bottom' ? this.bottom - (this.options.scaleLabel.fontSize / 2) : this.top + (this.options.scaleLabel.fontSize / 2);
-
- this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY);
- }
- } else {
- // Vertical
- if (this.options.gridLines.show) {}
-
- if (this.options.labels.show) {
- // Draw the labels
- }
+ });
- if (this.options.scaleLabel.show) {
- // Draw the scale label
- var scaleLabelX = this.options.position == 'left' ? this.left + (this.options.scaleLabel.fontSize / 2) : this.right - (this.options.scaleLabel.fontSize / 2);
- var scaleLabelY = this.top + ((this.bottom - this.top) / 2);
- var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
- this.ctx.save();
- this.ctx.translate(scaleLabelX, scaleLabelY);
- this.ctx.rotate(rotation);
- this.ctx.textAlign = "center";
- this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
- this.ctx.textBaseline = 'middle';
- this.ctx.fillText(this.options.scaleLabel.labelString, 0, 0);
- this.ctx.restore();
- }
- }
- }
- }
- });
Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig);
}).call(this);
beginAtZero: false,
override: null,
- // scale label
- scaleLabel: {
- fontColor: '#666',
- fontFamily: 'Helvetica Neue',
- fontSize: 12,
- fontStyle: 'normal',
-
- // actual label
- labelString: '',
-
- // display property
- show: false,
- },
-
// label settings
- labels: {
+ ticks: {
show: true,
mirror: false,
padding: 10,
}
};
- var LinearScale = Chart.Element.extend({
- isHorizontal: function() {
- return this.options.position == "top" || this.options.position == "bottom";
- },
+ var LinearScale = Chart.Scale.extend({
generateTicks: function(width, height) {
// We need to decide how many ticks we are going to have. Each tick draws a grid line.
// There are two possibilities. The first is that the user has manually overridden the scale
maxTicks = Math.min(11, Math.ceil(width / 50));
} else {
// The factor of 2 used to scale the font size has been experimentally determined.
- maxTicks = Math.min(11, Math.ceil(height / (2 * this.options.labels.fontSize)));
+ maxTicks = Math.min(11, Math.ceil(height / (2 * this.options.ticks.fontSize)));
}
// Make sure we always have at least 2 ticks
this.end = this.max;
}
},
- buildLabels: function() {
- // We assume that this has been run after ticks have been generated. We try to figure out
- // a label for each tick.
- this.labels = [];
-
- helpers.each(this.ticks, function(tick, index, ticks) {
- var label;
-
- if (this.options.labels.userCallback) {
- // If the user provided a callback for label generation, use that as first priority
- label = this.options.labels.userCallback(tick, index, ticks);
- } else if (this.options.labels.template) {
- // else fall back to the template string
- label = helpers.template(this.options.labels.template, {
- value: tick
- });
- }
-
- this.labels.push(label ? label : ""); // empty string will not render so we're good
- }, this);
- },
// Get the correct value. If the value type is object get the x or y based on whether we are horizontal or not
getRightValue: function(rawValue) {
- return (typeof (rawValue) === "object" && rawValue !== null) ? (this.isHorizontal() ? rawValue.x : rawValue.y) : rawValue;
+ return (typeof(rawValue) === "object" && rawValue !== null) ? (this.isHorizontal() ? rawValue.x : rawValue.y) : rawValue;
},
getPixelForValue: function(value) {
// This must be called after fit has been run so that
fit: function(maxWidth, maxHeight, margins) {
this.calculateRange();
this.generateTicks(maxWidth, maxHeight);
- this.buildLabels();
var minSize = {
width: 0,
minSize.height = maxHeight; // fill all the height
}
- // Are we showing a label for the scale?
- if (this.options.scaleLabel.show) {
- if (this.isHorizontal()) {
- minSize.height += (this.options.scaleLabel.fontSize * 1.5);
- } else {
- minSize.width += (this.options.scaleLabel.fontSize * 1.5);
- }
- }
-
this.paddingLeft = 0;
this.paddingRight = 0;
this.paddingTop = 0;
this.paddingBottom = 0;
- if (this.options.labels.show && this.options.display) {
- // Don't bother fitting the labels if we are not showing them
- var labelFont = helpers.fontString(this.options.labels.fontSize,
- this.options.labels.fontStyle, this.options.labels.fontFamily);
+ if (this.options.ticks.show && this.options.display) {
+ // Don't bother fitting the ticks if we are not showing them
+ var labelFont = helpers.fontString(this.options.ticks.fontSize,
+ this.options.ticks.fontStyle, this.options.ticks.fontFamily);
if (this.isHorizontal()) {
// A horizontal axis is more constrained by the height.
var maxLabelHeight = maxHeight - minSize.height;
- var labelHeight = 1.5 * this.options.labels.fontSize;
+ var labelHeight = 1.5 * this.options.ticks.fontSize;
minSize.height = Math.min(maxHeight, minSize.height + labelHeight);
- var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
+ var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
this.ctx.font = labelFont;
- var firstLabelWidth = this.ctx.measureText(this.labels[0]).width;
- var lastLabelWidth = this.ctx.measureText(this.labels[this.labels.length - 1]).width;
+ var firstLabelWidth = this.ctx.measureText(this.ticks[0]).width;
+ var lastLabelWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
- // Ensure that our labels are always inside the canvas
+ // Ensure that our ticks are always inside the canvas
this.paddingLeft = firstLabelWidth / 2;
this.paddingRight = lastLabelWidth / 2;
} else {
// A vertical axis is more constrained by the width. Labels are the dominant factor
// here, so get that length first
var maxLabelWidth = maxWidth - minSize.width;
- var largestTextWidth = helpers.longestText(this.ctx, labelFont, this.labels);
+ var largestTextWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
if (largestTextWidth < maxLabelWidth) {
// We don't need all the room
minSize.width = maxWidth;
}
- this.paddingTop = this.options.labels.fontSize / 2;
- this.paddingBottom = this.options.labels.fontSize / 2;
+ this.paddingTop = this.options.ticks.fontSize / 2;
+ this.paddingBottom = this.options.ticks.fontSize / 2;
}
}
var hasZero;
// Make sure we draw text in the correct color
- this.ctx.fillStyle = this.options.labels.fontColor;
+ this.ctx.fillStyle = this.options.ticks.fontColor;
if (this.isHorizontal()) {
if (this.options.gridLines.show) {
}, this);
}
- if (this.options.labels.show) {
- // Draw the labels
+ if (this.options.ticks.show) {
+ // Draw the ticks
var labelStartY;
}
this.ctx.textAlign = "center";
- this.ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
+ this.ctx.font = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
- helpers.each(this.labels, function(label, index) {
+ helpers.each(this.ticks, function(label, index) {
var xValue = this.getPixelForValue(this.ticks[index]);
this.ctx.fillText(label, xValue, labelStartY);
}, this);
}
-
- if (this.options.scaleLabel.show) {
- // Draw the scale label
- this.ctx.textAlign = "center";
- this.ctx.textBaseline = 'middle';
- this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
-
- var scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width
- var scaleLabelY = this.options.position == 'bottom' ? this.bottom - (this.options.scaleLabel.fontSize / 2) : this.top + (this.options.scaleLabel.fontSize / 2);
-
- this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY);
- }
} else {
// Vertical
if (this.options.gridLines.show) {
}, this);
}
- if (this.options.labels.show) {
- // Draw the labels
+ if (this.options.ticks.show) {
+ // Draw the ticks
var labelStartX;
if (this.options.position == "left") {
- if (this.options.labels.mirror) {
- labelStartX = this.right + this.options.labels.padding;
+ if (this.options.ticks.mirror) {
+ labelStartX = this.right + this.options.ticks.padding;
this.ctx.textAlign = "left";
} else {
- labelStartX = this.right - this.options.labels.padding;
+ labelStartX = this.right - this.options.ticks.padding;
this.ctx.textAlign = "right";
}
} else {
// right side
- if (this.options.labels.mirror) {
- labelStartX = this.left - this.options.labels.padding;
+ if (this.options.ticks.mirror) {
+ labelStartX = this.left - this.options.ticks.padding;
this.ctx.textAlign = "right";
} else {
- labelStartX = this.left + this.options.labels.padding;
+ labelStartX = this.left + this.options.ticks.padding;
this.ctx.textAlign = "left";
}
}
this.ctx.textBaseline = "middle";
- this.ctx.font = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
+ this.ctx.font = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
- helpers.each(this.labels, function(label, index) {
+ helpers.each(this.ticks, function(label, index) {
var yValue = this.getPixelForValue(this.ticks[index]);
this.ctx.fillText(label, labelStartX, yValue);
}, this);
}
-
- if (this.options.scaleLabel.show) {
- // Draw the scale label
- var scaleLabelX = this.options.position == 'left' ? this.left + (this.options.scaleLabel.fontSize / 2) : this.right - (this.options.scaleLabel.fontSize / 2);
- var scaleLabelY = this.top + ((this.bottom - this.top) / 2);
- var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
-
- this.ctx.save();
- this.ctx.translate(scaleLabelX, scaleLabelY);
- this.ctx.rotate(rotation);
- this.ctx.textAlign = "center";
- this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
- this.ctx.textBaseline = 'middle';
- this.ctx.fillText(this.options.scaleLabel.labelString, 0, 0);
- this.ctx.restore();
- }
}
}
}
displayFormat: false, // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
},
- // scale label
- scaleLabel: {
- fontColor: '#666',
- fontFamily: 'Helvetica Neue',
- fontSize: 12,
- fontStyle: 'normal',
-
- // actual label
- labelString: '',
-
- // display property
- show: false,
- },
-
// scale numbers
reverse: false,
override: null,
// label settings
- labels: {
+ ticks: {
show: true,
mirror: false,
padding: 10,
}
};
- var TimeScale = Chart.Element.extend({
- isHorizontal: function() {
- return this.options.position == "top" || this.options.position == "bottom";
- },
+ var TimeScale = Chart.Scale.extend({
parseTime: function(label) {
// Date objects
if (typeof label.getMonth === 'function' || typeof label == 'number') {
} else {
// Determine the smallest needed unit of the time
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
- var labelCapacity = innerWidth / this.options.labels.fontSize + 4;
+ var labelCapacity = innerWidth / this.options.ticks.fontSize + 4;
var buffer = this.options.tick.round ? 0 : 2;
this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, true) + buffer);
this.displayFormat = this.options.tick.displayFormat;
}
- // For every unit in between the first and last moment, create a moment and add it to the labels tick
- if (this.options.labels.userCallback) {
+ // For every unit in between the first and last moment, create a moment and add it to the ticks tick
+ if (this.options.ticks.userCallback) {
for (i = 0; i <= this.tickRange; i++) {
this.ticks.push(
- this.options.labels.userCallback(this.firstTick.clone()
+ this.options.ticks.userCallback(this.firstTick.clone()
.add(i, this.tickUnit)
.format(this.options.tick.displayFormat ? this.options.tick.displayFormat : time.unit[this.tickUnit].display)
)
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * this.options.spacing) + barWidth / 2;
},
- calculateTickRotation: function(maxHeight, margins) {
- //Get the width of each grid by calculating the difference
- //between x offsets between 0 and 1.
- var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
- this.ctx.font = labelFont;
+ // calculateTickRotation: function(maxHeight, margins) {
+ // //Get the width of each grid by calculating the difference
+ // //between x offsets between 0 and 1.
+ // var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
+ // this.ctx.font = labelFont;
- var firstWidth = this.ctx.measureText(this.ticks[0]).width;
- var lastWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
- var firstRotated;
- var lastRotated;
+ // var firstWidth = this.ctx.measureText(this.ticks[0]).width;
+ // var lastWidth = this.ctx.measureText(this.ticks[this.ticks.length - 1]).width;
+ // var firstRotated;
+ // var lastRotated;
- this.paddingRight = lastWidth / 2 + 3;
- this.paddingLeft = firstWidth / 2 + 3;
+ // this.paddingRight = lastWidth / 2 + 3;
+ // this.paddingLeft = firstWidth / 2 + 3;
- this.labelRotation = 0;
+ // this.labelRotation = 0;
- if (this.options.display) {
- var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
- var cosRotation;
- var sinRotation;
- var firstRotatedWidth;
+ // if (this.options.display) {
+ // var originalLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
+ // var cosRotation;
+ // var sinRotation;
+ // var firstRotatedWidth;
- this.labelWidth = originalLabelWidth;
+ // this.labelWidth = originalLabelWidth;
- //Allow 3 pixels x2 padding either side for label readability
- // only the index matters for a dataset scale, but we want a consistent interface between scales
+ // //Allow 3 pixels x2 padding either side for label readability
+ // // only the index matters for a dataset scale, but we want a consistent interface between scales
- var datasetWidth = Math.floor(this.getPixelForValue(null, 1 / this.ticks.length) - this.getPixelForValue(null, 0)) - 6;
+ // var datasetWidth = Math.floor(this.getPixelForValue(null, 1 / this.ticks.length) - this.getPixelForValue(null, 0)) - 6;
- //Max label rotation can be set or default to 90 - also act as a loop counter
- while (this.labelWidth > datasetWidth && this.labelRotation <= this.options.labels.maxRotation) {
- cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
- sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
+ // //Max label rotation can be set or default to 90 - also act as a loop counter
+ // while (this.labelWidth > datasetWidth && this.labelRotation <= this.options.ticks.maxRotation) {
+ // cosRotation = Math.cos(helpers.toRadians(this.labelRotation));
+ // sinRotation = Math.sin(helpers.toRadians(this.labelRotation));
- firstRotated = cosRotation * firstWidth;
- lastRotated = cosRotation * lastWidth;
+ // firstRotated = cosRotation * firstWidth;
+ // lastRotated = cosRotation * lastWidth;
- // We're right aligning the text now.
- if (firstRotated + this.options.labels.fontSize / 2 > this.yLabelWidth) {
- this.paddingLeft = firstRotated + this.options.labels.fontSize / 2;
- }
+ // // We're right aligning the text now.
+ // if (firstRotated + this.options.ticks.fontSize / 2 > this.yLabelWidth) {
+ // this.paddingLeft = firstRotated + this.options.ticks.fontSize / 2;
+ // }
- this.paddingRight = this.options.labels.fontSize / 2;
+ // this.paddingRight = this.options.ticks.fontSize / 2;
- if (sinRotation * originalLabelWidth > maxHeight) {
- // go back one step
- this.labelRotation--;
- break;
- }
+ // if (sinRotation * originalLabelWidth > maxHeight) {
+ // // go back one step
+ // this.labelRotation--;
+ // break;
+ // }
- this.labelRotation++;
- this.labelWidth = cosRotation * originalLabelWidth;
+ // this.labelRotation++;
+ // this.labelWidth = cosRotation * originalLabelWidth;
- }
- } else {
- this.labelWidth = 0;
- this.paddingRight = 0;
- this.paddingLeft = 0;
- }
+ // }
+ // } else {
+ // this.labelWidth = 0;
+ // this.paddingRight = 0;
+ // this.paddingLeft = 0;
+ // }
- if (margins) {
- this.paddingLeft -= margins.left;
- this.paddingRight -= margins.right;
+ // if (margins) {
+ // this.paddingLeft -= margins.left;
+ // this.paddingRight -= margins.right;
- this.paddingLeft = Math.max(this.paddingLeft, 0);
- this.paddingRight = Math.max(this.paddingRight, 0);
- }
+ // this.paddingLeft = Math.max(this.paddingLeft, 0);
+ // this.paddingRight = Math.max(this.paddingRight, 0);
+ // }
- },
+ // },
// Fit this axis to the given size
// @param {number} maxWidth : the max width the axis can be
// @param {number} maxHeight: the max height the axis can be
height: 0,
};
- // Are we showing a label for the scale?
- if (this.options.scaleLabel.show) {
- if (this.isHorizontal()) {
- minSize.height += (this.options.scaleLabel.fontSize * 1.5);
- } else {
- minSize.width += (this.options.scaleLabel.fontSize * 1.5);
- }
- }
-
- var labelFont = helpers.fontString(this.options.labels.fontSize, this.options.labels.fontStyle, this.options.labels.fontFamily);
+ var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
var longestLabelWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
// Width
if (this.isHorizontal()) {
minSize.width = maxWidth;
} else if (this.options.display) {
- var labelWidth = this.options.labels.show ? longestLabelWidth + 6 : 0;
- minSize.width += labelWidth;
- minSize.width = Math.min(minSize.width, maxWidth);
+ var labelWidth = this.options.ticks.show ? longestLabelWidth + 6 : 0;
+ minSize.width = Math.min(labelWidth, maxWidth);
}
// Height
if (this.isHorizontal() && this.options.display) {
- var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.labels.fontSize;
- minSize.height += this.options.labels.show ? labelHeight : 0;
- minSize.height = Math.min(minSize.height, maxHeight);
+ var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.ticks.fontSize;
+ minSize.height = Math.min(this.options.ticks.show ? labelHeight : 0, maxHeight);
} else if (this.options.display) {
minSize.height = maxHeight;
}
var setContextLineSettings;
// Make sure we draw text in the correct color
- this.ctx.fillStyle = this.options.labels.fontColor;
+ this.ctx.fillStyle = this.options.ticks.fontColor;
if (this.isHorizontal()) {
setContextLineSettings = true;
var isRotated = this.labelRotation !== 0;
var skipRatio = false;
- if ((this.options.labels.fontSize + 4) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) {
- skipRatio = 1 + Math.floor(((this.options.labels.fontSize + 4) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight)));
+ if ((this.options.ticks.fontSize + 4) * this.ticks.length > (this.width - (this.paddingLeft + this.paddingRight))) {
+ skipRatio = 1 + Math.floor(((this.options.ticks.fontSize + 4) * this.ticks.length) / (this.width - (this.paddingLeft + this.paddingRight)));
}
helpers.each(this.ticks, function(tick, index) {
this.ctx.stroke();
}
- if (this.options.labels.show) {
+ if (this.options.ticks.show) {
this.ctx.save();
this.ctx.translate(xLabelValue, (isRotated) ? this.top + 12 : this.top + 8);
this.ctx.rotate(helpers.toRadians(this.labelRotation) * -1);
this.ctx.restore();
}
}, this);
-
- if (this.options.scaleLabel.show) {
- // Draw the scale label
- this.ctx.textAlign = "center";
- this.ctx.textBaseline = 'middle';
- this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
-
- var scaleLabelX = this.left + ((this.right - this.left) / 2); // midpoint of the width
- var scaleLabelY = this.options.position == 'bottom' ? this.bottom - (this.options.scaleLabel.fontSize / 2) : this.top + (this.options.scaleLabel.fontSize / 2);
-
- this.ctx.fillText(this.options.scaleLabel.labelString, scaleLabelX, scaleLabelY);
- }
} else {
// Vertical
if (this.options.gridLines.show) {}
- if (this.options.labels.show) {
- // Draw the labels
+ if (this.options.ticks.show) {
+ // Draw the ticks
}
-
- if (this.options.scaleLabel.show) {
- // Draw the scale label
- var scaleLabelX = this.options.position == 'left' ? this.left + (this.options.scaleLabel.fontSize / 2) : this.right - (this.options.scaleLabel.fontSize / 2);
- var scaleLabelY = this.top + ((this.bottom - this.top) / 2);
- var rotation = this.options.position == 'left' ? -0.5 * Math.PI : 0.5 * Math.PI;
-
- this.ctx.save();
- this.ctx.translate(scaleLabelX, scaleLabelY);
- this.ctx.rotate(rotation);
- this.ctx.textAlign = "center";
- this.ctx.font = helpers.fontString(this.options.scaleLabel.fontSize, this.options.scaleLabel.fontStyle, this.options.scaleLabel.fontFamily);
- this.ctx.textBaseline = 'middle';
- this.ctx.fillText(this.options.scaleLabel.labelString, 0, 0);
- this.ctx.restore();
- }
}
}
}