mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Stuck on shared vertical scale draw logic
Trying to make all scales share the same draw function for both horizontal and vertical. For some reason the vertical linear scale on line.html has a width that is too small
This commit is contained in:
parent
9b9ebca246
commit
c7107677d2
@ -6,9 +6,9 @@
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<style>
|
||||
canvas {
|
||||
-webkit-box-shadow: 0 0 20px 0 rgba(0, 0, 0, .5);
|
||||
}
|
||||
canvas {
|
||||
-webkit-box-shadow: 0 0 20px 0 rgba(0, 0, 0, .5);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
@ -26,135 +26,134 @@
|
||||
<div>
|
||||
<h3>Legend</h3>
|
||||
<div id="legendContainer">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
fill: false,
|
||||
borderDash: [5, 5],
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'Month'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'Value'
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(config.data);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx, config);
|
||||
|
||||
updateLegend();
|
||||
};
|
||||
|
||||
function updateLegend() {
|
||||
$legendContainer = $('#legendContainer');
|
||||
$legendContainer.empty();
|
||||
$legendContainer.append(window.myLine.generateLegend());
|
||||
}
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data = dataset.data.map(function() {
|
||||
return randomScalingFactor();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
label: 'Dataset ' + config.data.datasets.length,
|
||||
borderColor: randomColor(0.4),
|
||||
backgroundColor: randomColor(0.5),
|
||||
pointBorderColor: randomColor(0.7),
|
||||
pointBackgroundColor: randomColor(0.5),
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
for (var index = 0; index < config.data.labels.length; ++index) {
|
||||
newDataset.data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
window.myLine.addDataset(newDataset);
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (config.data.datasets.length > 0) {
|
||||
config.data.labels.push('dataset #' + config.data.labels.length);
|
||||
|
||||
for (var index = 0; index < config.data.datasets.length; ++index) {
|
||||
window.myLine.addData(randomScalingFactor(), index);
|
||||
var config = {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
fill: false,
|
||||
borderDash: [5, 5],
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'Month'
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
display: true,
|
||||
scaleLabel: {
|
||||
show: true,
|
||||
labelString: 'Value'
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
updateLegend();
|
||||
}
|
||||
});
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
window.myLine.removeDataset(0);
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
config.data.labels.splice(-1, 1); // remove the label first
|
||||
|
||||
config.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||
window.myLine.removeData(datasetIndex, -1);
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
updateLegend();
|
||||
});
|
||||
console.log(config.data);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx, config);
|
||||
|
||||
updateLegend();
|
||||
};
|
||||
|
||||
function updateLegend() {
|
||||
$legendContainer = $('#legendContainer');
|
||||
$legendContainer.empty();
|
||||
$legendContainer.append(window.myLine.generateLegend());
|
||||
}
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
$.each(config.data.datasets, function(i, dataset) {
|
||||
dataset.data = dataset.data.map(function() {
|
||||
return randomScalingFactor();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
window.myLine.update();
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addDataset').click(function() {
|
||||
var newDataset = {
|
||||
label: 'Dataset ' + config.data.datasets.length,
|
||||
borderColor: randomColor(0.4),
|
||||
backgroundColor: randomColor(0.5),
|
||||
pointBorderColor: randomColor(0.7),
|
||||
pointBackgroundColor: randomColor(0.5),
|
||||
pointBorderWidth: 1,
|
||||
data: [],
|
||||
};
|
||||
|
||||
for (var index = 0; index < config.data.labels.length; ++index) {
|
||||
newDataset.data.push(randomScalingFactor());
|
||||
}
|
||||
|
||||
window.myLine.addDataset(newDataset);
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#addData').click(function() {
|
||||
if (config.data.datasets.length > 0) {
|
||||
config.data.labels.push('dataset #' + config.data.labels.length);
|
||||
|
||||
for (var index = 0; index < config.data.datasets.length; ++index) {
|
||||
window.myLine.addData(randomScalingFactor(), index);
|
||||
}
|
||||
|
||||
updateLegend();
|
||||
}
|
||||
});
|
||||
|
||||
$('#removeDataset').click(function() {
|
||||
window.myLine.removeDataset(0);
|
||||
updateLegend();
|
||||
});
|
||||
|
||||
$('#removeData').click(function() {
|
||||
config.data.labels.splice(-1, 1); // remove the label first
|
||||
|
||||
config.data.datasets.forEach(function(dataset, datasetIndex) {
|
||||
window.myLine.removeData(datasetIndex, -1);
|
||||
});
|
||||
|
||||
updateLegend();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
|
||||
@ -16,8 +16,8 @@
|
||||
type: "category",
|
||||
|
||||
// Specific to Bar Controller
|
||||
categoryPercentage: 0.8,
|
||||
barPercentage: 0.9,
|
||||
categoryPercentage: 0.75,
|
||||
barPercentage: 0.5,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
@ -101,6 +101,7 @@
|
||||
this.updateElement(rectangle, index, true, numBars);
|
||||
this.getDataset().metaData.splice(index, 0, rectangle);
|
||||
},
|
||||
|
||||
removeElement: function(index) {
|
||||
this.getDataset().metaData.splice(index, 1);
|
||||
},
|
||||
@ -110,6 +111,7 @@
|
||||
},
|
||||
|
||||
update: function(reset) {
|
||||
|
||||
var numBars = this.getBarCount();
|
||||
|
||||
var numData = this.getDataset().data.length;
|
||||
@ -218,37 +220,63 @@
|
||||
|
||||
},
|
||||
|
||||
calculateBarWidth: function() {
|
||||
getRuler: function() {
|
||||
|
||||
var xScale = this.getScaleForID(this.getDataset().xAxisID);
|
||||
var yScale = this.getScaleForID(this.getDataset().yAxisID);
|
||||
|
||||
var datasetCount = this.chart.data.datasets.length;
|
||||
var tickWidth = xScale.getSmallestDataDistance();
|
||||
console.log(tickWidth);
|
||||
var categoryWidth = tickWidth * xScale.options.categoryPercentage;
|
||||
var categorySpacing = (tickWidth - (tickWidth * xScale.options.categoryPercentage)) / 2;
|
||||
var fullBarWidth = categoryWidth / datasetCount;
|
||||
var barWidth = fullBarWidth * xScale.options.barPercentage;
|
||||
var barSpacing = fullBarWidth - (fullBarWidth * xScale.options.barPercentage);
|
||||
|
||||
return {
|
||||
datasetCount: datasetCount,
|
||||
tickWidth: tickWidth,
|
||||
categoryWidth: categoryWidth,
|
||||
categorySpacing: categorySpacing,
|
||||
fullBarWidth: fullBarWidth,
|
||||
barWidth: barWidth,
|
||||
barSpacing: barSpacing,
|
||||
};
|
||||
},
|
||||
|
||||
calculateBarWidth: function() {
|
||||
|
||||
var xScale = this.getScaleForID(this.getDataset().xAxisID);
|
||||
var ruler = this.getRuler();
|
||||
|
||||
if (xScale.options.stacked) {
|
||||
return xScale.ruler.categoryWidth;
|
||||
return ruler.categoryWidth;
|
||||
}
|
||||
|
||||
return xScale.ruler.barWidth;
|
||||
return ruler.barWidth;
|
||||
|
||||
},
|
||||
|
||||
|
||||
calculateBarX: function(datasetIndex, elementIndex) {
|
||||
|
||||
var xScale = this.getScaleForID(this.getDataset().xAxisID);
|
||||
var yScale = this.getScaleForID(this.getDataset().yAxisID);
|
||||
var xScale = this.getScaleForID(this.getDataset().xAxisID);
|
||||
|
||||
var leftTick = xScale.getPixelFromTickIndex(elementIndex);
|
||||
var ruler = this.getRuler();
|
||||
|
||||
if (yScale.options.stacked) {
|
||||
return leftTick + (xScale.ruler.categoryWidth / 2) + xScale.ruler.categorySpacing;
|
||||
return ruler.leftTick + (ruler.categoryWidth / 2) + ruler.categorySpacing;
|
||||
}
|
||||
|
||||
return leftTick +
|
||||
(xScale.ruler.barWidth / 2) +
|
||||
xScale.ruler.categorySpacing +
|
||||
(xScale.ruler.barWidth * datasetIndex) +
|
||||
(xScale.ruler.barSpacing / 2) +
|
||||
(xScale.ruler.barSpacing * datasetIndex);
|
||||
(ruler.barWidth / 2) +
|
||||
ruler.categorySpacing +
|
||||
(ruler.barWidth * datasetIndex) +
|
||||
(ruler.barSpacing / 2) +
|
||||
(ruler.barSpacing * datasetIndex);
|
||||
},
|
||||
|
||||
calculateBarY: function(datasetIndex, index) {
|
||||
|
||||
@ -125,9 +125,7 @@
|
||||
this.stop();
|
||||
var canvas = this.chart.canvas;
|
||||
var newWidth = helpers.getMaximumWidth(this.chart.canvas);
|
||||
var newHeight = (this.options.maintainAspectRatio && isNaN(this.chart.aspectRatio) === false && isFinite(this.chart.aspectRatio) && this.chart.aspectRatio !== 0)
|
||||
? newWidth / this.chart.aspectRatio
|
||||
: helpers.getMaximumHeight(this.chart.canvas);
|
||||
var newHeight = (this.options.maintainAspectRatio && isNaN(this.chart.aspectRatio) === false && isFinite(this.chart.aspectRatio) && this.chart.aspectRatio !== 0) ? newWidth / this.chart.aspectRatio : helpers.getMaximumHeight(this.chart.canvas);
|
||||
|
||||
canvas.width = this.chart.width = newWidth;
|
||||
canvas.height = this.chart.height = newHeight;
|
||||
@ -207,7 +205,7 @@
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
Chart.scaleService.update(this, this.chart.width, this.chart.height);
|
||||
},
|
||||
|
||||
buildOrUpdateControllers: function() {
|
||||
@ -230,7 +228,7 @@
|
||||
|
||||
update: function update(animationDuration, lazy) {
|
||||
// This will loop through any data and do the appropriate element update for the type
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
Chart.scaleService.update(this, this.chart.width, this.chart.height);
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.controller.update();
|
||||
}, this);
|
||||
@ -333,9 +331,9 @@
|
||||
for (var datasetIndex = 0; datasetIndex < this.data.datasets.length; datasetIndex++) {
|
||||
for (var elementIndex = 0; elementIndex < this.data.datasets[datasetIndex].metaData.length; elementIndex++) {
|
||||
if (this.data.datasets[datasetIndex].metaData[elementIndex].inLabelRange(eventPosition.x, eventPosition.y)) {
|
||||
helpers.each(this.data.datasets[datasetIndex].metaData, function(element, index) {
|
||||
elementsArray.push(element);
|
||||
}, this);
|
||||
helpers.each(this.data.datasets[datasetIndex].metaData, function(element, index) {
|
||||
elementsArray.push(element);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -490,7 +488,7 @@
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
|
||||
|
||||
// We only need to render at this point. Updating will cause scales to be recomputed generating flicker & using more
|
||||
// memory than necessary.
|
||||
this.render(this.options.hover.animationDuration, true);
|
||||
|
||||
@ -34,10 +34,69 @@
|
||||
};
|
||||
|
||||
Chart.Scale = Chart.Element.extend({
|
||||
isHorizontal: function() {
|
||||
return this.options.position == "top" || this.options.position == "bottom";
|
||||
|
||||
// These methods are ordered by lifecyle. Utilities then follow.
|
||||
// Any function defined here is inherited by all scale types.
|
||||
// Any function can be extended by the scale type
|
||||
|
||||
beforeUpdate: helpers.noop,
|
||||
update: function(maxWidth, maxHeight, margins) {
|
||||
|
||||
// Update Lifecycle - Probably don't want to ever extend or overwrite this function ;)
|
||||
this.beforeUpdate();
|
||||
|
||||
// Absorb the master measurements
|
||||
this.maxWidth = maxWidth;
|
||||
this.maxHeight = maxHeight;
|
||||
this.margins = margins;
|
||||
|
||||
// Dimensions
|
||||
this.beforeSetDimensions();
|
||||
this.setDimensions();
|
||||
this.afterSetDimensions();
|
||||
// Ticks
|
||||
this.beforeBuildTicks();
|
||||
this.buildTicks();
|
||||
this.afterBuildTicks();
|
||||
// Tick Rotation
|
||||
this.beforeCalculateTickRotation();
|
||||
this.calculateTickRotation();
|
||||
this.afterCalculateTickRotation();
|
||||
// Fit
|
||||
this.beforeFit();
|
||||
this.fit();
|
||||
this.afterFit();
|
||||
//
|
||||
this.afterUpdate();
|
||||
|
||||
return this.minSize;
|
||||
|
||||
},
|
||||
calculateTickRotation: function(maxHeight, margins) {
|
||||
afterUpdate: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeSetDimensions: helpers.noop,
|
||||
setDimensions: function() {
|
||||
// Set the unconstrained dimension before label rotation
|
||||
if (this.isHorizontal()) {
|
||||
this.width = this.maxWidth;
|
||||
} else {
|
||||
this.height = this.maxHeight;
|
||||
}
|
||||
},
|
||||
afterSetDimensions: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeBuildTicks: helpers.noop,
|
||||
buildTicks: helpers.noop,
|
||||
afterBuildTicks: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeCalculateTickRotation: helpers.noop,
|
||||
calculateTickRotation: function() {
|
||||
//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);
|
||||
@ -61,10 +120,10 @@
|
||||
|
||||
this.labelWidth = originalLabelWidth;
|
||||
|
||||
//Allow 3 pixels x2 padding either side for label readability
|
||||
// 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;
|
||||
var tickWidth = this.getPixelForTick(1) - this.getPixelForTick(0) - 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) {
|
||||
@ -81,7 +140,7 @@
|
||||
|
||||
this.paddingRight = this.options.ticks.fontSize / 2;
|
||||
|
||||
if (sinRotation * originalLabelWidth > maxHeight) {
|
||||
if (sinRotation * originalLabelWidth > this.maxHeight) {
|
||||
// go back one step
|
||||
this.labelRotation--;
|
||||
break;
|
||||
@ -98,36 +157,66 @@
|
||||
this.paddingLeft = 0;
|
||||
}
|
||||
|
||||
if (margins) {
|
||||
this.paddingLeft -= margins.left;
|
||||
this.paddingRight -= margins.right;
|
||||
if (this.margins) {
|
||||
this.paddingLeft -= this.margins.left;
|
||||
this.paddingRight -= this.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
|
||||
afterCalculateTickRotation: helpers.noop,
|
||||
|
||||
//
|
||||
|
||||
beforeFit: helpers.noop,
|
||||
fit: function() {
|
||||
|
||||
this.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()) {
|
||||
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));
|
||||
this.minSize.width = this.maxWidth;
|
||||
} else if (this.options.display) {
|
||||
var labelWidth = this.options.ticks.show ? longestLabelWidth + 6 : 0;
|
||||
this.minSize.width = Math.min(labelWidth, this.maxWidth);
|
||||
}
|
||||
|
||||
// Height
|
||||
if (this.isHorizontal() && this.options.display) {
|
||||
var labelHeight = (Math.sin(helpers.toRadians(this.labelRotation)) * longestLabelWidth) + 1.5 * this.options.ticks.fontSize;
|
||||
this.minSize.height = Math.min(this.options.ticks.show ? labelHeight : 0, this.maxHeight);
|
||||
} else if (this.options.display) {
|
||||
this.minSize.height = this.maxHeight;
|
||||
}
|
||||
|
||||
this.width = this.minSize.width;
|
||||
this.height = this.minSize.height;
|
||||
},
|
||||
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
|
||||
afterFit: helpers.noop,
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// Shared Methods
|
||||
isHorizontal: function() {
|
||||
return this.options.position == "top" || this.options.position == "bottom";
|
||||
},
|
||||
|
||||
// Used to get data value locations. Value can either be an index or a numerical value
|
||||
getPixelForValue: helpers.noop,
|
||||
|
||||
// Used for tick location, should
|
||||
getPixelForTick: function(index, includeOffset) {
|
||||
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);
|
||||
@ -141,9 +230,9 @@
|
||||
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
|
||||
|
||||
// Utility for getting the pixel location of a percentage of scale
|
||||
getPixelForDecimal: function(decimal, includeOffset) {
|
||||
if (this.isHorizontal()) {
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
var valueOffset = (innerWidth * decimal) + this.paddingLeft;
|
||||
@ -153,57 +242,15 @@
|
||||
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;
|
||||
var isRotated;
|
||||
var skipRatio;
|
||||
|
||||
// Make sure we draw text in the correct color
|
||||
this.ctx.fillStyle = this.options.ticks.fontColor;
|
||||
@ -212,8 +259,8 @@
|
||||
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;
|
||||
isRotated = this.labelRotation !== 0;
|
||||
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)));
|
||||
@ -224,8 +271,8 @@
|
||||
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)
|
||||
var xLineValue = this.getPixelForTick(index); // xvalues for grid lines
|
||||
var xLabelValue = this.getPixelForTick(index, this.options.gridLines.offsetGridLines); // x values for ticks (need to consider offsetLabel option)
|
||||
|
||||
if (this.options.gridLines.show) {
|
||||
if (index === 0) {
|
||||
@ -271,12 +318,67 @@
|
||||
}
|
||||
}, this);
|
||||
} else {
|
||||
// TODO Vertical
|
||||
if (this.options.gridLines.show) {}
|
||||
setContextLineSettings = true;
|
||||
var xTickStart = this.options.position == "left" ? this.right : this.left - 10;
|
||||
var xTickEnd = this.options.position == "left" ? this.right + 10 : this.left;
|
||||
isRotated = this.labelRotation !== 0;
|
||||
//skipRatio = false;
|
||||
|
||||
if (this.options.ticks.show) {
|
||||
// Draw the ticks
|
||||
}
|
||||
// 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(label, index) {
|
||||
// Blank ticks
|
||||
// if ((skipRatio > 1 && index % skipRatio > 0) || (label === undefined || label === null)) {
|
||||
// return;
|
||||
// }
|
||||
var yLineValue = this.getPixelForTick(index); // xvalues for grid lines
|
||||
var yLabelValue = this.getPixelForTick(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;
|
||||
}
|
||||
|
||||
yLineValue += helpers.aliasPixel(this.ctx.lineWidth);
|
||||
|
||||
// Draw the label area
|
||||
this.ctx.beginPath();
|
||||
|
||||
if (this.options.gridLines.drawTicks) {
|
||||
this.ctx.moveTo(xTickStart, yLineValue);
|
||||
this.ctx.lineTo(xTickEnd, yLineValue);
|
||||
}
|
||||
|
||||
// Draw the chart area
|
||||
if (this.options.gridLines.drawOnChartArea) {
|
||||
this.ctx.moveTo(chartArea.left, yLineValue);
|
||||
this.ctx.lineTo(chartArea.right, yLineValue);
|
||||
}
|
||||
|
||||
// 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(yLabelValue, (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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
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
|
||||
// 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
|
||||
@ -14,6 +14,7 @@
|
||||
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) {
|
||||
@ -27,7 +28,7 @@
|
||||
return this.defaults.hasOwnProperty(type) ? this.defaults[type] : {};
|
||||
},
|
||||
// The interesting function
|
||||
fitScalesForChart: function(chartInstance, width, height) {
|
||||
update: function(chartInstance, width, height) {
|
||||
var xPadding = width > 30 ? 5 : 2;
|
||||
var yPadding = height > 30 ? 5 : 2;
|
||||
|
||||
@ -78,7 +79,6 @@
|
||||
chartWidth -= (2 * xPadding);
|
||||
chartHeight -= (2 * yPadding);
|
||||
|
||||
|
||||
// Step 2
|
||||
var verticalScaleWidth = (width - chartWidth) / (leftScales.length + rightScales.length);
|
||||
|
||||
@ -89,7 +89,7 @@
|
||||
var minimumScaleSizes = [];
|
||||
|
||||
var verticalScaleMinSizeFunction = function(scaleInstance) {
|
||||
var minSize = scaleInstance.fit(verticalScaleWidth, chartHeight);
|
||||
var minSize = scaleInstance.update(verticalScaleWidth, chartHeight);
|
||||
minimumScaleSizes.push({
|
||||
horizontal: false,
|
||||
minSize: minSize,
|
||||
@ -98,7 +98,7 @@
|
||||
};
|
||||
|
||||
var horizontalScaleMinSizeFunction = function(scaleInstance) {
|
||||
var minSize = scaleInstance.fit(chartWidth, horizontalScaleHeight);
|
||||
var minSize = scaleInstance.update(chartWidth, horizontalScaleHeight);
|
||||
minimumScaleSizes.push({
|
||||
horizontal: true,
|
||||
minSize: minSize,
|
||||
@ -136,7 +136,7 @@
|
||||
});
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.fit(wrapper.minSize.width, maxChartHeight);
|
||||
scaleInstance.update(wrapper.minSize.width, maxChartHeight);
|
||||
}
|
||||
};
|
||||
|
||||
@ -153,7 +153,7 @@
|
||||
};
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.fit(maxChartWidth, wrapper.minSize.height, scaleMargin);
|
||||
scaleInstance.update(maxChartWidth, wrapper.minSize.height, scaleMargin);
|
||||
}
|
||||
};
|
||||
|
||||
@ -198,7 +198,7 @@
|
||||
};
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
|
||||
scaleInstance.update(wrapper.minSize.width, maxChartHeight, scaleMargin);
|
||||
}
|
||||
});
|
||||
|
||||
@ -215,7 +215,7 @@
|
||||
};
|
||||
|
||||
if (wrapper) {
|
||||
scaleInstance.fit(wrapper.minSize.width, maxChartHeight, scaleMargin);
|
||||
scaleInstance.update(wrapper.minSize.width, maxChartHeight, scaleMargin);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -12,30 +12,30 @@
|
||||
|
||||
var DatasetScale = Chart.Scale.extend({
|
||||
buildTicks: function(index) {
|
||||
this.ticks = [];
|
||||
this.ticks = this.data.labels;
|
||||
},
|
||||
|
||||
if (this.options.ticks.userCallback) {
|
||||
this.data.labels.forEach(function(labelString, index) {
|
||||
this.ticks.push(this.options.ticks.userCallback(labelString, index));
|
||||
}, this);
|
||||
// Used to get data value locations. Value can either be an index or a numerical value
|
||||
getPixelForValue: function(value, index, datasetIndex, includeOffset) {
|
||||
if (this.isHorizontal()) {
|
||||
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 toZero = this.max - this.min;
|
||||
var newVal = value - toZero;
|
||||
var decimal = newVal / (this.max - toZero);
|
||||
var valueOffset = (valueWidth * decimal) + this.paddingLeft;
|
||||
|
||||
if (this.options.gridLines.offsetGridLines && includeOffset) {
|
||||
valueOffset += (valueWidth / 2);
|
||||
}
|
||||
|
||||
return this.left + Math.round(valueOffset);
|
||||
} else {
|
||||
this.ticks = this.data.labels;
|
||||
return this.top + (index * (this.height / this.labels.length));
|
||||
}
|
||||
},
|
||||
buildRuler: function() {
|
||||
var datasetCount = this.data.datasets.length;
|
||||
|
||||
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);
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
|
||||
Chart.scaleService.registerScaleType("category", DatasetScale, defaultConfig);
|
||||
|
||||
}).call(this);
|
||||
|
||||
@ -6,109 +6,74 @@
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
display: true,
|
||||
position: "left",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.1)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
reverse: false,
|
||||
beginAtZero: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
ticks: {
|
||||
show: true,
|
||||
mirror: false,
|
||||
padding: 10,
|
||||
template: "<%=value.toLocaleString()%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue"
|
||||
}
|
||||
};
|
||||
|
||||
var LinearScale = Chart.Scale.extend({
|
||||
generateTicks: function(width, height) {
|
||||
buildTicks: function() {
|
||||
// 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
|
||||
// calculations in which case the job is easy. The other case is that we have to do it ourselves
|
||||
//
|
||||
// We assume at this point that the scale object has been updated with the following values
|
||||
// by the chart.
|
||||
// min: this is the minimum value of the scale
|
||||
// max: this is the maximum value of the scale
|
||||
// options: contains the options for the scale. This is referenced from the user settings
|
||||
// rather than being cloned. This ensures that updates always propogate to a redraw
|
||||
//
|
||||
// min: this is the minimum value of the scale
|
||||
// max: this is the maximum value of the scale
|
||||
// options: contains the options for the scale. This is referenced from the user settings
|
||||
// rather than being cloned. This ensures that updates always propogate to a redraw
|
||||
|
||||
// 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.ticks = [];
|
||||
this.min = this.maxWidth;
|
||||
this.max = this.maxHeight;
|
||||
|
||||
if (this.options.override) {
|
||||
// The user has specified the manual override. We use <= instead of < so that
|
||||
// we get the final line
|
||||
for (var i = 0; i <= this.options.override.steps; ++i) {
|
||||
var value = this.options.override.start + (i * this.options.override.stepWidth);
|
||||
this.ticks.push(value);
|
||||
}
|
||||
// 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(11, Math.ceil(this.width / 50));
|
||||
} else {
|
||||
// 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
|
||||
// The factor of 2 used to scale the font size has been experimentally determined.
|
||||
maxTicks = Math.min(11, Math.ceil(this.height / (2 * this.options.ticks.fontSize)));
|
||||
}
|
||||
|
||||
var maxTicks;
|
||||
// Make sure we always have at least 2 ticks
|
||||
maxTicks = Math.max(2, maxTicks);
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
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.ticks.fontSize)));
|
||||
// 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
|
||||
if (this.options.beginAtZero) {
|
||||
var minSign = helpers.sign(this.min);
|
||||
var maxSign = helpers.sign(this.max);
|
||||
|
||||
if (minSign < 0 && maxSign < 0) {
|
||||
// move the top up to 0
|
||||
this.max = 0;
|
||||
} else if (minSign > 0 && maxSign > 0) {
|
||||
// move the botttom down to 0
|
||||
this.min = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we always have at least 2 ticks
|
||||
maxTicks = Math.max(2, maxTicks);
|
||||
var niceRange = helpers.niceNum(this.max - this.min, false);
|
||||
var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
|
||||
var niceMin = Math.floor(this.min / spacing) * spacing;
|
||||
var niceMax = Math.ceil(this.max / spacing) * spacing;
|
||||
|
||||
// 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
|
||||
if (this.options.beginAtZero) {
|
||||
var minSign = helpers.sign(this.min);
|
||||
var maxSign = helpers.sign(this.max);
|
||||
|
||||
if (minSign < 0 && maxSign < 0) {
|
||||
// move the top up to 0
|
||||
this.max = 0;
|
||||
} else if (minSign > 0 && maxSign > 0) {
|
||||
// move the botttom down to 0
|
||||
this.min = 0;
|
||||
}
|
||||
}
|
||||
|
||||
var niceRange = helpers.niceNum(this.max - this.min, false);
|
||||
var spacing = helpers.niceNum(niceRange / (maxTicks - 1), true);
|
||||
var niceMin = Math.floor(this.min / spacing) * spacing;
|
||||
var niceMax = Math.ceil(this.max / spacing) * spacing;
|
||||
|
||||
// Put the values into the ticks array
|
||||
for (var j = niceMin; j <= niceMax; j += spacing) {
|
||||
this.ticks.push(j);
|
||||
}
|
||||
// Put the values into the ticks array
|
||||
for (var j = niceMin; j <= niceMax; j += spacing) {
|
||||
this.ticks.push(j);
|
||||
}
|
||||
|
||||
if (this.options.position == "left" || this.options.position == "right") {
|
||||
@ -131,11 +96,15 @@
|
||||
this.end = this.max;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
|
||||
// Utils
|
||||
// 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;
|
||||
},
|
||||
getPixelForValue: function(value) {
|
||||
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
|
||||
var pixel;
|
||||
@ -155,428 +124,425 @@
|
||||
},
|
||||
|
||||
// Functions needed for line charts
|
||||
calculateRange: function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
// calculateRange: function() {
|
||||
// this.min = null;
|
||||
// this.max = null;
|
||||
|
||||
var positiveValues = [];
|
||||
var negativeValues = [];
|
||||
// var positiveValues = [];
|
||||
// var negativeValues = [];
|
||||
|
||||
if (this.options.stacked) {
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(rawValue, index) {
|
||||
// if (this.options.stacked) {
|
||||
// helpers.each(this.data.datasets, function(dataset) {
|
||||
// if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) {
|
||||
// helpers.each(dataset.data, function(rawValue, index) {
|
||||
|
||||
var value = this.getRightValue(rawValue);
|
||||
// var value = this.getRightValue(rawValue);
|
||||
|
||||
positiveValues[index] = positiveValues[index] || 0;
|
||||
negativeValues[index] = negativeValues[index] || 0;
|
||||
// positiveValues[index] = positiveValues[index] || 0;
|
||||
// negativeValues[index] = negativeValues[index] || 0;
|
||||
|
||||
if (this.options.relativePoints) {
|
||||
positiveValues[index] = 100;
|
||||
} else {
|
||||
if (value < 0) {
|
||||
negativeValues[index] += value;
|
||||
} else {
|
||||
positiveValues[index] += value;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
// if (this.options.relativePoints) {
|
||||
// positiveValues[index] = 100;
|
||||
// } else {
|
||||
// if (value < 0) {
|
||||
// negativeValues[index] += value;
|
||||
// } else {
|
||||
// positiveValues[index] += value;
|
||||
// }
|
||||
// }
|
||||
// }, this);
|
||||
// }
|
||||
// }, this);
|
||||
|
||||
var values = positiveValues.concat(negativeValues);
|
||||
this.min = helpers.min(values);
|
||||
this.max = helpers.max(values);
|
||||
// var values = positiveValues.concat(negativeValues);
|
||||
// this.min = helpers.min(values);
|
||||
// this.max = helpers.max(values);
|
||||
|
||||
} else {
|
||||
helpers.each(this.data.datasets, function(dataset) {
|
||||
if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(rawValue, index) {
|
||||
var value = this.getRightValue(rawValue);
|
||||
// } else {
|
||||
// helpers.each(this.data.datasets, function(dataset) {
|
||||
// if (this.isHorizontal() ? dataset.xAxisID === this.id : dataset.yAxisID === this.id) {
|
||||
// helpers.each(dataset.data, function(rawValue, index) {
|
||||
// var value = this.getRightValue(rawValue);
|
||||
|
||||
if (this.min === null) {
|
||||
this.min = value;
|
||||
} else if (value < this.min) {
|
||||
this.min = value;
|
||||
}
|
||||
// if (this.min === null) {
|
||||
// this.min = value;
|
||||
// } else if (value < this.min) {
|
||||
// this.min = value;
|
||||
// }
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value;
|
||||
} else if (value > this.max) {
|
||||
this.max = value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
// if (this.max === null) {
|
||||
// this.max = value;
|
||||
// } else if (value > this.max) {
|
||||
// this.max = value;
|
||||
// }
|
||||
// }, this);
|
||||
// }
|
||||
// }, this);
|
||||
// }
|
||||
|
||||
if (this.min === this.max) {
|
||||
this.min--;
|
||||
this.max++;
|
||||
}
|
||||
},
|
||||
// if (this.min === this.max) {
|
||||
// this.min--;
|
||||
// this.max++;
|
||||
// }
|
||||
// },
|
||||
|
||||
getPointPixelForValue: function(rawValue, index, datasetIndex) {
|
||||
var value = this.getRightValue(rawValue);
|
||||
// getPointPixelForValue: function(rawValue, index, datasetIndex) {
|
||||
// var value = this.getRightValue(rawValue);
|
||||
|
||||
if (this.options.stacked) {
|
||||
var offsetPos = 0;
|
||||
var offsetNeg = 0;
|
||||
// if (this.options.stacked) {
|
||||
// var offsetPos = 0;
|
||||
// var offsetNeg = 0;
|
||||
|
||||
for (var i = this.data.datasets.length - 1; i > datasetIndex; --i) {
|
||||
if (this.data.datasets[i].data[index] < 0) {
|
||||
offsetNeg += this.data.datasets[i].data[index];
|
||||
} else {
|
||||
offsetPos += this.data.datasets[i].data[index];
|
||||
}
|
||||
}
|
||||
// for (var i = this.data.datasets.length - 1; i > datasetIndex; --i) {
|
||||
// if (this.data.datasets[i].data[index] < 0) {
|
||||
// offsetNeg += this.data.datasets[i].data[index];
|
||||
// } else {
|
||||
// offsetPos += this.data.datasets[i].data[index];
|
||||
// }
|
||||
// }
|
||||
|
||||
if (value < 0) {
|
||||
return this.getPixelForValue(offsetNeg + value);
|
||||
} else {
|
||||
return this.getPixelForValue(offsetPos + value);
|
||||
}
|
||||
} else {
|
||||
return this.getPixelForValue(value);
|
||||
}
|
||||
},
|
||||
// if (value < 0) {
|
||||
// return this.getPixelForValue(offsetNeg + value);
|
||||
// } else {
|
||||
// return this.getPixelForValue(offsetPos + value);
|
||||
// }
|
||||
// } else {
|
||||
// return this.getPixelForValue(value);
|
||||
// }
|
||||
// },
|
||||
|
||||
// Functions needed for bar charts
|
||||
calculateBarBase: function(datasetIndex, index) {
|
||||
var base = 0;
|
||||
// calculateBarBase: function(datasetIndex, index) {
|
||||
// var base = 0;
|
||||
|
||||
if (this.options.stacked) {
|
||||
// if (this.options.stacked) {
|
||||
|
||||
var value = this.data.datasets[datasetIndex].data[index];
|
||||
// var value = this.data.datasets[datasetIndex].data[index];
|
||||
|
||||
if (value < 0) {
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (this.data.datasets[i].yAxisID === this.id) {
|
||||
base += this.data.datasets[i].data[index] < 0 ? this.data.datasets[i].data[index] : 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (var j = 0; j < datasetIndex; j++) {
|
||||
if (this.data.datasets[j].yAxisID === this.id) {
|
||||
base += this.data.datasets[j].data[index] > 0 ? this.data.datasets[j].data[index] : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (value < 0) {
|
||||
// for (var i = 0; i < datasetIndex; i++) {
|
||||
// if (this.data.datasets[i].yAxisID === this.id) {
|
||||
// base += this.data.datasets[i].data[index] < 0 ? this.data.datasets[i].data[index] : 0;
|
||||
// }
|
||||
// }
|
||||
// } else {
|
||||
// for (var j = 0; j < datasetIndex; j++) {
|
||||
// if (this.data.datasets[j].yAxisID === this.id) {
|
||||
// base += this.data.datasets[j].data[index] > 0 ? this.data.datasets[j].data[index] : 0;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return this.getPixelForValue(base);
|
||||
}
|
||||
// return this.getPixelForValue(base);
|
||||
// }
|
||||
|
||||
base = this.getPixelForValue(this.min);
|
||||
// base = this.getPixelForValue(this.min);
|
||||
|
||||
if (this.beginAtZero || ((this.min <= 0 && this.max >= 0) || (this.min >= 0 && this.max <= 0))) {
|
||||
base = this.getPixelForValue(0);
|
||||
base += this.options.gridLines.lineWidth;
|
||||
} else if (this.min < 0 && this.max < 0) {
|
||||
// All values are negative. Use the top as the base
|
||||
base = this.getPixelForValue(this.max);
|
||||
}
|
||||
// if (this.beginAtZero || ((this.min <= 0 && this.max >= 0) || (this.min >= 0 && this.max <= 0))) {
|
||||
// base = this.getPixelForValue(0);
|
||||
// base += this.options.gridLines.lineWidth;
|
||||
// } else if (this.min < 0 && this.max < 0) {
|
||||
// // All values are negative. Use the top as the base
|
||||
// base = this.getPixelForValue(this.max);
|
||||
// }
|
||||
|
||||
return base;
|
||||
// return base;
|
||||
|
||||
},
|
||||
calculateBarY: function(datasetIndex, index) {
|
||||
var value = this.data.datasets[datasetIndex].data[index];
|
||||
// },
|
||||
// calculateBarY: function(datasetIndex, index) {
|
||||
// var value = this.data.datasets[datasetIndex].data[index];
|
||||
|
||||
if (this.options.stacked) {
|
||||
// if (this.options.stacked) {
|
||||
|
||||
var sumPos = 0,
|
||||
sumNeg = 0;
|
||||
// var sumPos = 0,
|
||||
// sumNeg = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (this.data.datasets[i].data[index] < 0) {
|
||||
sumNeg += this.data.datasets[i].data[index] || 0;
|
||||
} else {
|
||||
sumPos += this.data.datasets[i].data[index] || 0;
|
||||
}
|
||||
}
|
||||
// for (var i = 0; i < datasetIndex; i++) {
|
||||
// if (this.data.datasets[i].data[index] < 0) {
|
||||
// sumNeg += this.data.datasets[i].data[index] || 0;
|
||||
// } else {
|
||||
// sumPos += this.data.datasets[i].data[index] || 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (value < 0) {
|
||||
return this.getPixelForValue(sumNeg + value);
|
||||
} else {
|
||||
return this.getPixelForValue(sumPos + value);
|
||||
}
|
||||
// if (value < 0) {
|
||||
// return this.getPixelForValue(sumNeg + value);
|
||||
// } else {
|
||||
// return this.getPixelForValue(sumPos + value);
|
||||
// }
|
||||
|
||||
return this.getPixelForValue(value);
|
||||
}
|
||||
// return this.getPixelForValue(value);
|
||||
// }
|
||||
|
||||
return this.getPixelForValue(value);
|
||||
},
|
||||
// return this.getPixelForValue(value);
|
||||
// },
|
||||
|
||||
// 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) {
|
||||
this.calculateRange();
|
||||
this.generateTicks(maxWidth, maxHeight);
|
||||
// fit: function() {
|
||||
|
||||
var minSize = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
// this.minSize = {
|
||||
// width: 0,
|
||||
// height: 0,
|
||||
// };
|
||||
|
||||
// In a horizontal axis, we need some room for the scale to be drawn
|
||||
//
|
||||
// -----------------------------------------------------
|
||||
// | | | | |
|
||||
//
|
||||
// In a vertical axis, we need some room for the scale to be drawn.
|
||||
// The actual grid lines will be drawn on the chart area, however, we need to show
|
||||
// ticks where the axis actually is.
|
||||
// We will allocate 25px for this width
|
||||
// |
|
||||
// -|
|
||||
// |
|
||||
// |
|
||||
// -|
|
||||
// |
|
||||
// |
|
||||
// -|
|
||||
// // In a horizontal axis, we need some room for the scale to be drawn
|
||||
// //
|
||||
// // -----------------------------------------------------
|
||||
// // | | | | |
|
||||
// //
|
||||
// // In a vertical axis, we need some room for the scale to be drawn.
|
||||
// // The actual grid lines will be drawn on the chart area, however, we need to show
|
||||
// // ticks where the axis actually is.
|
||||
// // We will allocate 25px for this width
|
||||
// // |
|
||||
// // -|
|
||||
// // |
|
||||
// // |
|
||||
// // -|
|
||||
// // |
|
||||
// // |
|
||||
// // -|
|
||||
|
||||
|
||||
// Width
|
||||
if (this.isHorizontal()) {
|
||||
minSize.width = maxWidth; // fill all the width
|
||||
} else {
|
||||
minSize.width = this.options.gridLines.show && this.options.display ? 10 : 0;
|
||||
}
|
||||
// // Width
|
||||
// if (this.isHorizontal()) {
|
||||
// this.minSize.width = this.maxWidth; // fill all the width
|
||||
// } else {
|
||||
// this.minSize.width = this.options.gridLines.show && this.options.display ? 10 : 0;
|
||||
// }
|
||||
|
||||
// height
|
||||
if (this.isHorizontal()) {
|
||||
minSize.height = this.options.gridLines.show && this.options.display ? 10 : 0;
|
||||
} else {
|
||||
minSize.height = maxHeight; // fill all the height
|
||||
}
|
||||
// // height
|
||||
// if (this.isHorizontal()) {
|
||||
// this.minSize.height = this.options.gridLines.show && this.options.display ? 10 : 0;
|
||||
// } else {
|
||||
// this.minSize.height = this.maxHeight; // fill all the height
|
||||
// }
|
||||
|
||||
this.paddingLeft = 0;
|
||||
this.paddingRight = 0;
|
||||
this.paddingTop = 0;
|
||||
this.paddingBottom = 0;
|
||||
// this.paddingLeft = 0;
|
||||
// this.paddingRight = 0;
|
||||
// this.paddingTop = 0;
|
||||
// this.paddingBottom = 0;
|
||||
|
||||
|
||||
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.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.ticks.fontSize;
|
||||
minSize.height = Math.min(maxHeight, minSize.height + labelHeight);
|
||||
// if (this.isHorizontal()) {
|
||||
// // A horizontal axis is more constrained by the height.
|
||||
// var maxLabelHeight = this.maxHeight - this.minSize.height;
|
||||
// var labelHeight = 1.5 * this.options.ticks.fontSize;
|
||||
// this.minSize.height = Math.min(this.maxHeight, this.minSize.height + labelHeight);
|
||||
|
||||
var labelFont = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
|
||||
this.ctx.font = labelFont;
|
||||
// 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.ticks[0]).width;
|
||||
var lastLabelWidth = this.ctx.measureText(this.ticks[this.ticks.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 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.ticks);
|
||||
// // 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 = this.maxWidth - this.minSize.width;
|
||||
// var largestTextWidth = helpers.longestText(this.ctx, labelFont, this.ticks);
|
||||
|
||||
if (largestTextWidth < maxLabelWidth) {
|
||||
// We don't need all the room
|
||||
minSize.width += largestTextWidth;
|
||||
minSize.width += 3; // extra padding
|
||||
} else {
|
||||
// Expand to max size
|
||||
minSize.width = maxWidth;
|
||||
}
|
||||
// if (largestTextWidth < maxLabelWidth) {
|
||||
// // We don't need all the room
|
||||
// this.minSize.width += largestTextWidth;
|
||||
// this.minSize.width += 3; // extra padding
|
||||
// } else {
|
||||
// // Expand to max size
|
||||
// this.minSize.width = this.maxWidth;
|
||||
// }
|
||||
|
||||
this.paddingTop = this.options.ticks.fontSize / 2;
|
||||
this.paddingBottom = this.options.ticks.fontSize / 2;
|
||||
}
|
||||
}
|
||||
// this.paddingTop = this.options.ticks.fontSize / 2;
|
||||
// this.paddingBottom = this.options.ticks.fontSize / 2;
|
||||
// }
|
||||
// }
|
||||
|
||||
if (margins) {
|
||||
this.paddingLeft -= margins.left;
|
||||
this.paddingTop -= margins.top;
|
||||
this.paddingRight -= margins.right;
|
||||
this.paddingBottom -= margins.bottom;
|
||||
// if (this.margins) {
|
||||
// this.paddingLeft -= this.margins.left;
|
||||
// this.paddingTop -= this.margins.top;
|
||||
// this.paddingRight -= this.margins.right;
|
||||
// this.paddingBottom -= this.margins.bottom;
|
||||
|
||||
this.paddingLeft = Math.max(this.paddingLeft, 0);
|
||||
this.paddingTop = Math.max(this.paddingTop, 0);
|
||||
this.paddingRight = Math.max(this.paddingRight, 0);
|
||||
this.paddingBottom = Math.max(this.paddingBottom, 0);
|
||||
}
|
||||
// this.paddingLeft = Math.max(this.paddingLeft, 0);
|
||||
// this.paddingTop = Math.max(this.paddingTop, 0);
|
||||
// this.paddingRight = Math.max(this.paddingRight, 0);
|
||||
// this.paddingBottom = Math.max(this.paddingBottom, 0);
|
||||
// }
|
||||
|
||||
this.width = minSize.width;
|
||||
this.height = minSize.height;
|
||||
return minSize;
|
||||
},
|
||||
// this.width = this.minSize.width;
|
||||
// this.height = this.minSize.height;
|
||||
// },
|
||||
// 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) {
|
||||
// draw: function(chartArea) {
|
||||
// if (this.options.display) {
|
||||
|
||||
var setContextLineSettings;
|
||||
var hasZero;
|
||||
// var setContextLineSettings;
|
||||
// var hasZero;
|
||||
|
||||
// Make sure we draw text in the correct color
|
||||
this.ctx.fillStyle = this.options.ticks.fontColor;
|
||||
// // Make sure we draw text in the correct color
|
||||
// this.ctx.fillStyle = this.options.ticks.fontColor;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
if (this.options.gridLines.show) {
|
||||
// Draw the horizontal line
|
||||
setContextLineSettings = true;
|
||||
hasZero = helpers.findNextWhere(this.ticks, function(tick) {
|
||||
return tick === 0;
|
||||
}) !== undefined;
|
||||
var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 5;
|
||||
var yTickEnd = this.options.position == "bottom" ? this.top + 5 : this.bottom;
|
||||
// if (this.isHorizontal()) {
|
||||
// if (this.options.gridLines.show) {
|
||||
// // Draw the horizontal line
|
||||
// setContextLineSettings = true;
|
||||
// hasZero = helpers.findNextWhere(this.ticks, function(tick) {
|
||||
// return tick === 0;
|
||||
// }) !== undefined;
|
||||
// var yTickStart = this.options.position == "bottom" ? this.top : this.bottom - 5;
|
||||
// var yTickEnd = this.options.position == "bottom" ? this.top + 5 : this.bottom;
|
||||
|
||||
helpers.each(this.ticks, function(tick, index) {
|
||||
// Grid lines are vertical
|
||||
var xValue = this.getPixelForValue(tick);
|
||||
// helpers.each(this.ticks, function(tick, index) {
|
||||
// // Grid lines are vertical
|
||||
// var xValue = this.getPixelForValue(tick);
|
||||
|
||||
if (tick === 0 || (!hasZero && index === 0)) {
|
||||
// Draw the 0 point specially or the left if there is no 0
|
||||
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;
|
||||
}
|
||||
// if (tick === 0 || (!hasZero && index === 0)) {
|
||||
// // Draw the 0 point specially or the left if there is no 0
|
||||
// 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;
|
||||
// }
|
||||
|
||||
xValue += helpers.aliasPixel(this.ctx.lineWidth);
|
||||
// xValue += helpers.aliasPixel(this.ctx.lineWidth);
|
||||
|
||||
// Draw the label area
|
||||
this.ctx.beginPath();
|
||||
// // Draw the label area
|
||||
// this.ctx.beginPath();
|
||||
|
||||
if (this.options.gridLines.drawTicks) {
|
||||
this.ctx.moveTo(xValue, yTickStart);
|
||||
this.ctx.lineTo(xValue, yTickEnd);
|
||||
}
|
||||
// if (this.options.gridLines.drawTicks) {
|
||||
// this.ctx.moveTo(xValue, yTickStart);
|
||||
// this.ctx.lineTo(xValue, yTickEnd);
|
||||
// }
|
||||
|
||||
// Draw the chart area
|
||||
if (this.options.gridLines.drawOnChartArea) {
|
||||
this.ctx.moveTo(xValue, chartArea.top);
|
||||
this.ctx.lineTo(xValue, chartArea.bottom);
|
||||
}
|
||||
// // Draw the chart area
|
||||
// if (this.options.gridLines.drawOnChartArea) {
|
||||
// this.ctx.moveTo(xValue, chartArea.top);
|
||||
// this.ctx.lineTo(xValue, chartArea.bottom);
|
||||
// }
|
||||
|
||||
// Need to stroke in the loop because we are potentially changing line widths & colours
|
||||
this.ctx.stroke();
|
||||
}, this);
|
||||
}
|
||||
// // Need to stroke in the loop because we are potentially changing line widths & colours
|
||||
// this.ctx.stroke();
|
||||
// }, this);
|
||||
// }
|
||||
|
||||
if (this.options.ticks.show) {
|
||||
// Draw the ticks
|
||||
// if (this.options.ticks.show) {
|
||||
// // Draw the ticks
|
||||
|
||||
var labelStartY;
|
||||
// var labelStartY;
|
||||
|
||||
if (this.options.position == "top") {
|
||||
labelStartY = this.bottom - 10;
|
||||
this.ctx.textBaseline = "bottom";
|
||||
} else {
|
||||
// bottom side
|
||||
labelStartY = this.top + 10;
|
||||
this.ctx.textBaseline = "top";
|
||||
}
|
||||
// if (this.options.position == "top") {
|
||||
// labelStartY = this.bottom - 10;
|
||||
// this.ctx.textBaseline = "bottom";
|
||||
// } else {
|
||||
// // bottom side
|
||||
// labelStartY = this.top + 10;
|
||||
// this.ctx.textBaseline = "top";
|
||||
// }
|
||||
|
||||
this.ctx.textAlign = "center";
|
||||
this.ctx.font = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
|
||||
// this.ctx.textAlign = "center";
|
||||
// this.ctx.font = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
|
||||
|
||||
helpers.each(this.ticks, function(label, index) {
|
||||
var xValue = this.getPixelForValue(this.ticks[index]);
|
||||
this.ctx.fillText(label, xValue, labelStartY);
|
||||
}, this);
|
||||
}
|
||||
} else {
|
||||
// Vertical
|
||||
if (this.options.gridLines.show) {
|
||||
// helpers.each(this.ticks, function(label, index) {
|
||||
// var xValue = this.getPixelForValue(this.ticks[index]);
|
||||
// this.ctx.fillText(label, xValue, labelStartY);
|
||||
// }, this);
|
||||
// }
|
||||
// } else {
|
||||
// // Vertical
|
||||
// if (this.options.gridLines.show) {
|
||||
|
||||
// Draw the vertical line
|
||||
setContextLineSettings = true;
|
||||
hasZero = helpers.findNextWhere(this.ticks, function(tick) {
|
||||
return tick === 0;
|
||||
}) !== undefined;
|
||||
var xTickStart = this.options.position == "right" ? this.left : this.right - 5;
|
||||
var xTickEnd = this.options.position == "right" ? this.left + 5 : this.right;
|
||||
// // Draw the vertical line
|
||||
// setContextLineSettings = true;
|
||||
// hasZero = helpers.findNextWhere(this.ticks, function(tick) {
|
||||
// return tick === 0;
|
||||
// }) !== undefined;
|
||||
// var xTickStart = this.options.position == "right" ? this.left : this.right - 5;
|
||||
// var xTickEnd = this.options.position == "right" ? this.left + 5 : this.right;
|
||||
|
||||
helpers.each(this.ticks, function(tick, index) {
|
||||
// Grid lines are horizontal
|
||||
var yValue = this.getPixelForValue(tick);
|
||||
// helpers.each(this.ticks, function(tick, index) {
|
||||
// // Grid lines are horizontal
|
||||
// var yValue = this.getPixelForValue(tick);
|
||||
|
||||
if (tick === 0 || (!hasZero && index === 0)) {
|
||||
// Draw the 0 point specially or the bottom if there is no 0
|
||||
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; // use boolean to indicate that we only want to do this once
|
||||
}
|
||||
// if (tick === 0 || (!hasZero && index === 0)) {
|
||||
// // Draw the 0 point specially or the bottom if there is no 0
|
||||
// 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; // use boolean to indicate that we only want to do this once
|
||||
// }
|
||||
|
||||
yValue += helpers.aliasPixel(this.ctx.lineWidth);
|
||||
// yValue += helpers.aliasPixel(this.ctx.lineWidth);
|
||||
|
||||
// Draw the label area
|
||||
this.ctx.beginPath();
|
||||
// // Draw the label area
|
||||
// this.ctx.beginPath();
|
||||
|
||||
if (this.options.gridLines.drawTicks) {
|
||||
this.ctx.moveTo(xTickStart, yValue);
|
||||
this.ctx.lineTo(xTickEnd, yValue);
|
||||
}
|
||||
// if (this.options.gridLines.drawTicks) {
|
||||
// this.ctx.moveTo(xTickStart, yValue);
|
||||
// this.ctx.lineTo(xTickEnd, yValue);
|
||||
// }
|
||||
|
||||
// Draw the chart area
|
||||
if (this.options.gridLines.drawOnChartArea) {
|
||||
this.ctx.moveTo(chartArea.left, yValue);
|
||||
this.ctx.lineTo(chartArea.right, yValue);
|
||||
}
|
||||
// // Draw the chart area
|
||||
// if (this.options.gridLines.drawOnChartArea) {
|
||||
// this.ctx.moveTo(chartArea.left, yValue);
|
||||
// this.ctx.lineTo(chartArea.right, yValue);
|
||||
// }
|
||||
|
||||
this.ctx.stroke();
|
||||
}, this);
|
||||
}
|
||||
// this.ctx.stroke();
|
||||
// }, this);
|
||||
// }
|
||||
|
||||
if (this.options.ticks.show) {
|
||||
// Draw the ticks
|
||||
// if (this.options.ticks.show) {
|
||||
// // Draw the ticks
|
||||
|
||||
var labelStartX;
|
||||
// var labelStartX;
|
||||
|
||||
if (this.options.position == "left") {
|
||||
if (this.options.ticks.mirror) {
|
||||
labelStartX = this.right + this.options.ticks.padding;
|
||||
this.ctx.textAlign = "left";
|
||||
} else {
|
||||
labelStartX = this.right - this.options.ticks.padding;
|
||||
this.ctx.textAlign = "right";
|
||||
}
|
||||
} else {
|
||||
// right side
|
||||
if (this.options.ticks.mirror) {
|
||||
labelStartX = this.left - this.options.ticks.padding;
|
||||
this.ctx.textAlign = "right";
|
||||
} else {
|
||||
labelStartX = this.left + this.options.ticks.padding;
|
||||
this.ctx.textAlign = "left";
|
||||
}
|
||||
}
|
||||
// if (this.options.position == "left") {
|
||||
// if (this.options.ticks.mirror) {
|
||||
// labelStartX = this.right + this.options.ticks.padding;
|
||||
// this.ctx.textAlign = "left";
|
||||
// } else {
|
||||
// labelStartX = this.right - this.options.ticks.padding;
|
||||
// this.ctx.textAlign = "right";
|
||||
// }
|
||||
// } else {
|
||||
// // right side
|
||||
// if (this.options.ticks.mirror) {
|
||||
// labelStartX = this.left - this.options.ticks.padding;
|
||||
// this.ctx.textAlign = "right";
|
||||
// } else {
|
||||
// labelStartX = this.left + this.options.ticks.padding;
|
||||
// this.ctx.textAlign = "left";
|
||||
// }
|
||||
// }
|
||||
|
||||
this.ctx.textBaseline = "middle";
|
||||
this.ctx.font = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
|
||||
// this.ctx.textBaseline = "middle";
|
||||
// this.ctx.font = helpers.fontString(this.options.ticks.fontSize, this.options.ticks.fontStyle, this.options.ticks.fontFamily);
|
||||
|
||||
helpers.each(this.ticks, function(label, index) {
|
||||
var yValue = this.getPixelForValue(this.ticks[index]);
|
||||
this.ctx.fillText(label, labelStartX, yValue);
|
||||
}, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// helpers.each(this.ticks, function(label, index) {
|
||||
// var yValue = this.getPixelForValue(this.ticks[index]);
|
||||
// this.ctx.fillText(label, labelStartX, yValue);
|
||||
// }, this);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
});
|
||||
Chart.scaleService.registerScaleType("linear", LinearScale, defaultConfig);
|
||||
|
||||
|
||||
@ -58,41 +58,14 @@
|
||||
};
|
||||
|
||||
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, // draw ticks extending towards the label
|
||||
},
|
||||
|
||||
tick: {
|
||||
time: {
|
||||
format: false, // false == date objects or use pattern string from http://momentjs.com/docs/#/parsing/string-format/
|
||||
unit: false, // false == automatic or override with week, month, year, etc.
|
||||
round: false, // none, or override with week, month, year, etc.
|
||||
displayFormat: false, // defaults to unit's corresponding unitFormat below or override using pattern string from http://momentjs.com/docs/#/displaying/format/
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
reverse: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
ticks: {
|
||||
show: true,
|
||||
mirror: false,
|
||||
padding: 10,
|
||||
template: "<%=value.toLocaleString()%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
maxRotation: 45,
|
||||
}
|
||||
};
|
||||
|
||||
var TimeScale = Chart.Scale.extend({
|
||||
@ -106,11 +79,11 @@
|
||||
return label;
|
||||
}
|
||||
// Custom parsing (return an instance of moment)
|
||||
if (typeof this.options.tick.format !== 'string' && this.options.tick.format.call) {
|
||||
return this.options.tick.format(label);
|
||||
if (typeof this.options.time.format !== 'string' && this.options.time.format.call) {
|
||||
return this.options.time.format(label);
|
||||
}
|
||||
// Moment format parsing
|
||||
return moment(label, this.options.tick.format);
|
||||
return moment(label, this.options.time.format);
|
||||
},
|
||||
generateTicks: function(index) {
|
||||
|
||||
@ -120,8 +93,8 @@
|
||||
// Parse each label into a moment
|
||||
this.data.labels.forEach(function(label, index) {
|
||||
var labelMoment = this.parseTime(label);
|
||||
if (this.options.tick.round) {
|
||||
labelMoment.startOf(this.options.tick.round);
|
||||
if (this.options.time.round) {
|
||||
labelMoment.startOf(this.options.time.round);
|
||||
}
|
||||
this.labelMoments.push(labelMoment);
|
||||
}, this);
|
||||
@ -131,15 +104,15 @@
|
||||
this.lastTick = moment.max.call(this, this.labelMoments).clone();
|
||||
|
||||
// Set unit override if applicable
|
||||
if (this.options.tick.unit) {
|
||||
this.tickUnit = this.options.tick.unit || 'day';
|
||||
if (this.options.time.unit) {
|
||||
this.tickUnit = this.options.time.unit || 'day';
|
||||
this.displayFormat = time.unit.day.display;
|
||||
this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, this.tickUnit, true));
|
||||
} else {
|
||||
// Determine the smallest needed unit of the time
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
var labelCapacity = innerWidth / this.options.ticks.fontSize + 4;
|
||||
var buffer = this.options.tick.round ? 0 : 2;
|
||||
var buffer = this.options.time.round ? 0 : 2;
|
||||
|
||||
this.tickRange = Math.ceil(this.lastTick.diff(this.firstTick, true) + buffer);
|
||||
var done;
|
||||
@ -167,8 +140,8 @@
|
||||
|
||||
|
||||
// Tick displayFormat override
|
||||
if (this.options.tick.displayFormat) {
|
||||
this.displayFormat = this.options.tick.displayFormat;
|
||||
if (this.options.time.displayFormat) {
|
||||
this.displayFormat = this.options.time.displayFormat;
|
||||
}
|
||||
|
||||
// For every unit in between the first and last moment, create a moment and add it to the ticks tick
|
||||
@ -177,7 +150,7 @@
|
||||
this.ticks.push(
|
||||
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)
|
||||
.format(this.options.time.displayFormat ? this.options.time.displayFormat : time.unit[this.tickUnit].display)
|
||||
)
|
||||
);
|
||||
}
|
||||
@ -185,14 +158,20 @@
|
||||
for (i = 0; i <= this.tickRange; i++) {
|
||||
this.ticks.push(this.firstTick.clone()
|
||||
.add(i, this.tickUnit)
|
||||
.format(this.options.tick.displayFormat ? this.options.tick.displayFormat : time.unit[this.tickUnit].display)
|
||||
.format(this.options.time.displayFormat ? this.options.time.displayFormat : time.unit[this.tickUnit].display)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
getPixelForValue: function(value, decimal, datasetIndex, includeOffset) {
|
||||
getSmallestDataDistance: function() {
|
||||
return this.smallestLabelSeparation;
|
||||
},
|
||||
getPixelForValue: function(value, 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
|
||||
|
||||
var decimal = 0.5;
|
||||
|
||||
if (this.isHorizontal()) {
|
||||
var innerWidth = this.width - (this.paddingLeft + this.paddingRight);
|
||||
var valueWidth = innerWidth / Math.max(this.ticks.length - 1, 1);
|
||||
@ -203,46 +182,46 @@
|
||||
return this.top + (decimal * (this.height / this.ticks.length));
|
||||
}
|
||||
},
|
||||
getPointPixelForValue: function(value, index, datasetIndex) {
|
||||
// getPointPixelForValue: function(value, index, datasetIndex) {
|
||||
|
||||
var offset = this.labelMoments[index].diff(this.firstTick, this.tickUnit, true);
|
||||
return this.getPixelForValue(value, offset / this.tickRange, datasetIndex);
|
||||
},
|
||||
// var offset = this.labelMoments[index].diff(this.firstTick, this.tickUnit, true);
|
||||
// return this.getPixelForValue(value, offset / this.tickRange, datasetIndex);
|
||||
// },
|
||||
|
||||
// Functions needed for bar charts
|
||||
calculateBaseWidth: function() {
|
||||
// // Functions needed for bar charts
|
||||
// calculateBaseWidth: function() {
|
||||
|
||||
var base = this.getPixelForValue(null, this.smallestLabelSeparation / this.tickRange, 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 = this.calculateBaseWidth() - ((barDatasetCount - 1) * this.options.spacing);
|
||||
// var base = this.getPixelForValue(null, this.smallestLabelSeparation / this.tickRange, 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 = this.calculateBaseWidth() - ((barDatasetCount - 1) * this.options.spacing);
|
||||
|
||||
if (this.options.stacked) {
|
||||
return Math.max(baseWidth, 1);
|
||||
}
|
||||
return Math.max((baseWidth / barDatasetCount), 1);
|
||||
},
|
||||
calculateBarX: function(barDatasetCount, datasetIndex, elementIndex) {
|
||||
// if (this.options.stacked) {
|
||||
// return Math.max(baseWidth, 1);
|
||||
// }
|
||||
// return Math.max((baseWidth / barDatasetCount), 1);
|
||||
// },
|
||||
// calculateBarX: function(barDatasetCount, datasetIndex, elementIndex) {
|
||||
|
||||
var xWidth = this.calculateBaseWidth(),
|
||||
offset = this.labelMoments[elementIndex].diff(this.firstTick, this.tickUnit, true),
|
||||
xAbsolute = this.getPixelForValue(null, offset / this.tickRange, datasetIndex, true) - (xWidth / 2),
|
||||
barWidth = this.calculateBarWidth(barDatasetCount);
|
||||
// var xWidth = this.calculateBaseWidth(),
|
||||
// offset = this.labelMoments[elementIndex].diff(this.firstTick, this.tickUnit, true),
|
||||
// xAbsolute = this.getPixelForValue(null, offset / this.tickRange, datasetIndex, true) - (xWidth / 2),
|
||||
// barWidth = this.calculateBarWidth(barDatasetCount);
|
||||
|
||||
if (this.options.stacked) {
|
||||
return xAbsolute + barWidth / 2;
|
||||
}
|
||||
// if (this.options.stacked) {
|
||||
// return xAbsolute + barWidth / 2;
|
||||
// }
|
||||
|
||||
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * this.options.spacing) + barWidth / 2;
|
||||
},
|
||||
// return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * this.options.spacing) + barWidth / 2;
|
||||
// },
|
||||
|
||||
// calculateTickRotation: function(maxHeight, margins) {
|
||||
// //Get the width of each grid by calculating the difference
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user