Chart.js/src/core/core.scale.js

306 lines
13 KiB
JavaScript
Raw Normal View History

(function() {
2015-05-27 18:28:00 +02:00
"use strict";
var root = this,
Chart = root.Chart,
helpers = Chart.helpers;
2015-05-27 18:28:00 +02:00
// 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 = {
// The interesting function
fitScalesForChart: function(chartInstance, width, height) {
2015-06-13 00:08:27 +02:00
var xPadding = width > 30 ? 5 : 2;
var yPadding = height > 30 ? 5 : 2;
2015-05-27 18:28:00 +02:00
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";
});
2015-06-12 22:48:20 +02:00
var visibleLeftScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "left";
});
var visibleRightScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "right";
});
var visibleTopScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "top";
});
var visibleBottomScales = helpers.where(chartInstance.scales, function(scaleInstance) {
return scaleInstance.options.position == "bottom";
});
// // Adjust the padding to take into account displaying labels
// if (topScales.length === 0 || bottomScales.length === 0) {
// var maxFontHeight = 0;
2015-05-27 18:28:00 +02:00
2015-06-12 22:48:20 +02:00
// var maxFontHeightFunction = function(scaleInstance) {
// if (scaleInstance.options.labels.show) {
// // Only consider font sizes for axes that actually show labels
// maxFontHeight = Math.max(maxFontHeight, scaleInstance.options.labels.fontSize);
// }
// };
2015-05-27 18:28:00 +02:00
2015-06-12 22:48:20 +02:00
// helpers.each(leftScales, maxFontHeightFunction);
// helpers.each(rightScales, maxFontHeightFunction);
2015-05-27 18:28:00 +02:00
2015-06-12 22:48:20 +02:00
// if (topScales.length === 0) {
// // Add padding so that we can handle drawing the top nicely
// yPadding += 0.75 * maxFontHeight; // 0.75 since padding added on both sides
// }
2015-05-27 18:28:00 +02:00
2015-06-12 22:48:20 +02:00
// if (bottomScales.length === 0) {
// // Add padding so that we can handle drawing the bottom nicely
// yPadding += 1.5 * maxFontHeight;
// }
// }
2015-05-27 18:28:00 +02:00
// 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
// |------------------------------------------------------|
2015-06-12 22:48:20 +02:00
// | | T1 | |
2015-05-27 18:28:00 +02:00
// |----|-----|-------------------------------------|-----|
2015-06-12 22:48:20 +02:00
// | | | | |
// | L1 | L2 | Chart area | R1 |
// | | | | |
// | | | | |
2015-05-27 18:28:00 +02:00
// |----|-----|-------------------------------------|-----|
2015-06-12 22:48:20 +02:00
// | | B1 | |
// | | | |
2015-05-27 18:28:00 +02:00
// |------------------------------------------------------|
// 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);
2015-06-12 22:48:20 +02:00
2015-05-27 18:28:00 +02:00
// 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,
};
2015-05-27 18:28:00 +02:00
if (wrapper) {
scaleInstance.fit(maxChartWidth, wrapper.minSize.height, scaleMargin);
2015-05-27 18:28:00 +02:00
}
};
var totalLeftWidth = xPadding;
var totalRightWidth = xPadding;
2015-05-27 18:28:00 +02:00
var totalTopHeight = yPadding;
var totalBottomHeight = yPadding;
helpers.each(leftScales, verticalScaleFitFunction);
helpers.each(rightScales, verticalScaleFitFunction);
2015-05-27 18:28:00 +02:00
// Figure out how much margin is on the left and right of the horizontal axes
2015-05-27 18:28:00 +02:00
helpers.each(leftScales, function(scaleInstance) {
totalLeftWidth += scaleInstance.width;
});
helpers.each(rightScales, function(scaleInstance) {
totalRightWidth += scaleInstance.width;
});
helpers.each(topScales, horizontalScaleFitFunction);
helpers.each(bottomScales, horizontalScaleFitFunction);
2015-05-27 18:28:00 +02:00
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;
});
2015-05-27 18:28:00 +02:00
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);
}
});
// Step 7
2015-05-27 18:28:00 +02:00
// 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,
};
}
}
};
// 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
Chart.scales = {
constructors: {},
// Use a registration function so that we can move to an ES6 map when we no longer need to support
// old browsers
2015-06-13 00:08:27 +02:00
registerScaleType: function(type, scaleConstructor) {
this.constructors[type] = scaleConstructor;
2015-05-27 18:28:00 +02:00
},
2015-06-13 00:08:27 +02:00
getScaleConstructor: function(type) {
return this.constructors.hasOwnProperty(type) ? this.constructors[type] : undefined;
2015-05-27 18:28:00 +02:00
}
};
}).call(this);