mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
Merge pull request #1132 from etimberg/feature/v2.0dev-xy
Scale rewrite for v2.0
This commit is contained in:
commit
48dd1cf024
132
samples/bar-multi-axis.html
Normal file
132
samples/bar-multi-axis.html
Normal file
@ -0,0 +1,132 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Bar Chart Multi Axis</title>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width: 50%">
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</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 barChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: 'Dataset 1',
|
||||
backgroundColor: "rgba(220,220,220,0.5)",
|
||||
yAxisID: "y-axis-1",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
yAxisID: "y-axis-2",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
yAxisID: "y-axis-1",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx).Bar({
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
hoverAnimationDuration: 400,
|
||||
stacked: false,
|
||||
scales: {
|
||||
yAxes: [{
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}, {
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "right",
|
||||
id: "y-axis-2",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: false, // only want the grid lines for one axis to show up
|
||||
drawTicks: false,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#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();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -8,7 +8,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width: 100%">
|
||||
<div style="width: 50%">
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
@ -47,12 +47,15 @@
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx).Bar(barChartData, {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
scaleBeginAtZero: false,
|
||||
hoverAnimationDuration: 400,
|
||||
stacked: true,
|
||||
window.myBar = new Chart(ctx).Bar({
|
||||
data: barChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
scaleBeginAtZero: false,
|
||||
hoverAnimationDuration: 400,
|
||||
stacked: true,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -48,7 +48,12 @@
|
||||
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myDoughnut = new Chart(ctx).Doughnut(doughnutData, {responsive : true});
|
||||
window.myDoughnut = new Chart(ctx).Doughnut({
|
||||
data: doughnutData,
|
||||
options: {
|
||||
responsive : true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -58,8 +58,11 @@
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myDoughnut = new Chart(ctx).Doughnut(doughnutData, {
|
||||
responsive: true
|
||||
window.myDoughnut = new Chart(ctx).Doughnut({
|
||||
data: doughnutData,
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -112,10 +112,13 @@
|
||||
|
||||
window.onload = function() {
|
||||
var ctx1 = document.getElementById("chart1").getContext("2d");
|
||||
window.myLine = new Chart(ctx1).Line(lineChartData, {
|
||||
showScale: false,
|
||||
pointDot : true,
|
||||
responsive: true
|
||||
window.myLine = new Chart(ctx1).Line({
|
||||
data: lineChartData,
|
||||
options: {
|
||||
showScale: false,
|
||||
pointDot : true,
|
||||
responsive: true
|
||||
}
|
||||
});
|
||||
|
||||
var ctx2 = document.getElementById("chart2").getContext("2d");
|
||||
|
||||
140
samples/line-multi-axis.html
Normal file
140
samples/line-multi-axis.html
Normal file
@ -0,0 +1,140 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Line Chart Multiple Axes</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%">
|
||||
<div>
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
};
|
||||
var randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var lineChartData = {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
yAxisID: "y-axis-1",
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
yAxisID: "y-axis-2"
|
||||
}]
|
||||
};
|
||||
|
||||
$.each(lineChartData.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(1);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(lineChartData);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx).Line({
|
||||
data: lineChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
stacked: false,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
gridLines: {
|
||||
offsetGridLines: false
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}, {
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "right",
|
||||
id: "y-axis-2",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: false, // only want the grid lines for one axis to show up
|
||||
drawTicks: false,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
lineChartData.datasets[0].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
lineChartData.datasets[1].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
window.myLine.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -8,7 +8,7 @@
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:100%">
|
||||
<div style="width:50%">
|
||||
<div>
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
@ -45,10 +45,20 @@
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx).Line(lineChartData, {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
stacked: true
|
||||
window.myLine = new Chart(ctx).Line({
|
||||
data: lineChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
stacked: false,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
gridLines: {
|
||||
offsetGridLines: false
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -145,10 +145,14 @@
|
||||
|
||||
window.onload = function() {
|
||||
var ctx1 = document.getElementById("chart-area1").getContext("2d");
|
||||
window.myPie = new Chart(ctx1).Pie(pieData);
|
||||
window.myPie = new Chart(ctx1).Pie({
|
||||
data: pieData
|
||||
});
|
||||
|
||||
var ctx2 = document.getElementById("chart-area2").getContext("2d");
|
||||
window.myPie = new Chart(ctx2).Pie(pieData);
|
||||
window.myPie = new Chart(ctx2).Pie({
|
||||
data: pieData
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
|
||||
@ -52,7 +52,9 @@
|
||||
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myPie = new Chart(ctx).Pie(pieData);
|
||||
window.myPie = new Chart(ctx).Pie({
|
||||
data: pieData
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function(){
|
||||
|
||||
@ -53,8 +53,11 @@
|
||||
|
||||
window.onload = function(){
|
||||
var ctx = document.getElementById("chart-area").getContext("2d");
|
||||
window.myPolarArea = new Chart(ctx).PolarArea(polarData, {
|
||||
responsive:true
|
||||
window.myPolarArea = new Chart(ctx).PolarArea({
|
||||
data: polarData,
|
||||
options: {
|
||||
responsive:true
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -44,8 +44,11 @@
|
||||
};
|
||||
|
||||
window.onload = function(){
|
||||
window.myRadar = new Chart(document.getElementById("canvas").getContext("2d")).Radar(radarChartData, {
|
||||
responsive: true
|
||||
window.myRadar = new Chart(document.getElementById("canvas").getContext("2d")).Radar({
|
||||
data: radarChartData,
|
||||
options: {
|
||||
responsive: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
223
samples/scatter-multi-axis.html
Normal file
223
samples/scatter-multi-axis.html
Normal file
@ -0,0 +1,223 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Scatter Chart Multi Axis</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%">
|
||||
<div>
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
</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 randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var scatterChartData = {
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
xAxisID: "x-axis-1",
|
||||
yAxisID: "y-axis-1",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
xAxisID: "x-axis-1",
|
||||
yAxisID: "y-axis-2",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
$.each(scatterChartData.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.1);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(scatterChartData);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myScatter = new Chart(ctx).Scatter({
|
||||
data: scatterChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'single',
|
||||
scales: {
|
||||
xAxes: [{
|
||||
position: "bottom",
|
||||
gridLines: {
|
||||
zeroLineColor: "rgba(0,0,0,1)"
|
||||
}
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}, {
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "right",
|
||||
id: "y-axis-2",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: false, // only want the grid lines for one axis to show up
|
||||
drawTicks: false,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
scatterChartData.datasets[0].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}];
|
||||
scatterChartData.datasets[1].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
window.myScatter.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
155
samples/scatter.html
Normal file
155
samples/scatter.html
Normal file
@ -0,0 +1,155 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Scatter Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div style="width:50%">
|
||||
<div>
|
||||
<canvas id="canvas" height="450" width="600"></canvas>
|
||||
</div>
|
||||
</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 randomColor = function(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (opacity || '.3') + ')';
|
||||
};
|
||||
|
||||
var scatterChartData = {
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
}]
|
||||
};
|
||||
|
||||
$.each(scatterChartData.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.1);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
});
|
||||
|
||||
console.log(scatterChartData);
|
||||
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myScatter = new Chart(ctx).Scatter({
|
||||
data: scatterChartData,
|
||||
options: {
|
||||
responsive: true,
|
||||
hoverMode: 'single', // should always use single for a scatter chart
|
||||
scales: {
|
||||
xAxes: [{
|
||||
gridLines: {
|
||||
zeroLineColor: "rgba(0,0,0,1)"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
scatterChartData.datasets[0].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}];
|
||||
scatterChartData.datasets[1].data = [{
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}, {
|
||||
x: randomScalingFactor(),
|
||||
y: randomScalingFactor(),
|
||||
}]
|
||||
window.myScatter.update();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
493
src/Chart.Bar.js
493
src/Chart.Bar.js
@ -7,23 +7,73 @@
|
||||
|
||||
|
||||
var defaultConfig = {
|
||||
//Boolean - Whether the scale should start at zero, or an order of magnitude down from the lowest value
|
||||
scaleBeginAtZero: true,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
display: true,
|
||||
position: "bottom",
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
offsetGridLines: true,
|
||||
},
|
||||
|
||||
//Boolean - Whether grid lines are shown across the chart
|
||||
scaleShowGridLines: true,
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
//String - Colour of the grid lines
|
||||
scaleGridLineColor: "rgba(0,0,0,.05)",
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
//Number - Width of the grid lines
|
||||
scaleGridLineWidth: 1,
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
//Boolean - Whether to show horizontal lines (except X axis)
|
||||
scaleShowHorizontalLines: true,
|
||||
|
||||
//Boolean - Whether to show vertical lines (except Y axis)
|
||||
scaleShowVerticalLines: true,
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
},
|
||||
|
||||
//Number - Pixel width of the bar border
|
||||
barBorderWidth: 2,
|
||||
@ -46,144 +96,44 @@
|
||||
Chart.Type.extend({
|
||||
name: "Bar",
|
||||
defaults: defaultConfig,
|
||||
initialize: function(data) {
|
||||
|
||||
// Save data as a source for updating of values & methods
|
||||
this.data = data;
|
||||
|
||||
var options = this.options;
|
||||
|
||||
var _this = this;
|
||||
|
||||
// Custom Scale Methods and Options
|
||||
this.ScaleClass = Chart.Scale.extend({
|
||||
offsetGridLines: true,
|
||||
calculateBarBase: function(datasetIndex, index) {
|
||||
|
||||
var base = 0;
|
||||
|
||||
if (_this.options.stacked) {
|
||||
var bar = _this.data.datasets[datasetIndex].metaData[index];
|
||||
if (bar.value < 0) {
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
base += _this.data.datasets[i].metaData[index].value < base ? _this.data.datasets[i].metaData[index].value : 0;
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
base += _this.data.datasets[i].metaData[index].value > base ? _this.data.datasets[i].metaData[index].value : 0;
|
||||
}
|
||||
}
|
||||
return this.calculateY(base);
|
||||
}
|
||||
|
||||
base = this.endPoint;
|
||||
|
||||
if (this.beginAtZero || ((this.min <= 0 && this.max >= 0) || (this.min >= 0 && this.max <= 0))) {
|
||||
base = this.calculateY(0);
|
||||
base += _this.options.scaleGridLineWidth;
|
||||
} else if (this.min < 0 && this.max < 0) {
|
||||
// All values are negative. Use the top as the base
|
||||
base = this.startPoint;
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
},
|
||||
calculateBarX: function(datasetCount, datasetIndex, elementIndex) {
|
||||
var xWidth = this.calculateBaseWidth(),
|
||||
xAbsolute = this.calculateX(elementIndex) - (xWidth / 2),
|
||||
barWidth = this.calculateBarWidth(datasetCount);
|
||||
|
||||
if (_this.options.stacked) {
|
||||
return xAbsolute + barWidth / 2;
|
||||
}
|
||||
|
||||
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * options.barDatasetSpacing) + barWidth / 2;
|
||||
},
|
||||
calculateBarY: function(datasets, datasetIndex, barIndex, value) {
|
||||
|
||||
if (_this.options.stacked) {
|
||||
|
||||
var sumPos = 0,
|
||||
sumNeg = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (datasets[i].metaData[barIndex].value < 0) {
|
||||
sumNeg += datasets[i].metaData[barIndex].value || 0;
|
||||
} else {
|
||||
sumPos += datasets[i].metaData[barIndex].value || 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return this.calculateY(sumNeg + value);
|
||||
} else {
|
||||
return this.calculateY(sumPos + value);
|
||||
}
|
||||
|
||||
/*if (options.relativeBars) {
|
||||
offset = offset / sum * 100;
|
||||
}*/
|
||||
|
||||
return this.calculateY(0);
|
||||
}
|
||||
|
||||
var offset = 0;
|
||||
|
||||
for (i = datasetIndex; i < datasets.length; i++) {
|
||||
if (i === datasetIndex && value) {
|
||||
offset += value;
|
||||
} else {
|
||||
offset = offset + (datasets[i].metaData[barIndex].value);
|
||||
}
|
||||
}
|
||||
|
||||
return this.calculateY(value);
|
||||
},
|
||||
calculateBaseWidth: function() {
|
||||
return (this.calculateX(1) - this.calculateX(0)) - (2 * options.barValueSpacing);
|
||||
},
|
||||
calculateBaseHeight: function() {
|
||||
return (this.calculateY(1) - this.calculateY(0));
|
||||
},
|
||||
calculateBarWidth: function(datasetCount) {
|
||||
|
||||
//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
|
||||
var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * options.barDatasetSpacing);
|
||||
|
||||
if (_this.options.stacked) {
|
||||
return baseWidth;
|
||||
}
|
||||
return (baseWidth / datasetCount);
|
||||
},
|
||||
});
|
||||
|
||||
initialize: function() {
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.tooltipEvents, this.onHover);
|
||||
helpers.bindEvents(this, this.options.events, this.onHover);
|
||||
|
||||
//Declare the extension of the default point, to cater for the options passed in to the constructor
|
||||
this.BarClass = Chart.Rectangle.extend({
|
||||
ctx: this.chart.ctx,
|
||||
});
|
||||
|
||||
// Build Scale
|
||||
this.buildScale(this.data.labels);
|
||||
|
||||
//Create a new bar for each piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaData = [];
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new this.BarClass());
|
||||
}, this);
|
||||
|
||||
// The bar chart only supports a single x axis because the x axis is always a dataset axis
|
||||
dataset.xAxisID = this.options.scales.xAxes[0].id;
|
||||
|
||||
if (!dataset.yAxisID) {
|
||||
dataset.yAxisID = this.options.scales.yAxes[0].id;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Build and fit the scale. Needs to happen after the axis IDs have been set
|
||||
this.buildScale();
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Set defaults for bars
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
helpers.extend(bar, {
|
||||
base: this.scale.zeroPoint,
|
||||
width: this.scale.calculateBarWidth(this.data.datasets.length),
|
||||
x: this.scale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: this.scale.calculateBarY(this.data.datasets, datasetIndex, index, this.data.datasets[datasetIndex].data[index]),
|
||||
base: yScale.getPixelForValue(0),
|
||||
width: xScale.calculateBarWidth(this.data.datasets.length),
|
||||
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: yScale.calculateBarY(this.data.datasets, datasetIndex, index, this.data.datasets[datasetIndex].data[index]),
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
});
|
||||
@ -321,22 +271,26 @@
|
||||
return this;
|
||||
},
|
||||
update: function() {
|
||||
|
||||
this.scale.update();
|
||||
// Update the scale sizes
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
helpers.extend(bar, {
|
||||
value: this.data.datasets[datasetIndex].data[index],
|
||||
});
|
||||
|
||||
bar.pivot();
|
||||
}, this);
|
||||
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
helpers.extend(bar, {
|
||||
base: this.scale.calculateBarBase(datasetIndex, index),
|
||||
x: this.scale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: this.scale.calculateBarY(this.data.datasets, datasetIndex, index, this.data.datasets[datasetIndex].data[index]),
|
||||
width: this.scale.calculateBarWidth(this.data.datasets.length),
|
||||
base: yScale.calculateBarBase(datasetIndex, index),
|
||||
x: xScale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: yScale.calculateBarY(this.data.datasets, datasetIndex, index, this.data.datasets[datasetIndex].data[index]),
|
||||
width: xScale.calculateBarWidth(this.data.datasets.length),
|
||||
label: this.data.labels[index],
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
borderColor: this.data.datasets[datasetIndex].borderColor,
|
||||
@ -345,94 +299,214 @@
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
});
|
||||
|
||||
bar.pivot();
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function(labels) {
|
||||
var self = this;
|
||||
var self = this;
|
||||
|
||||
var dataTotal = function() {
|
||||
var values = [];
|
||||
// Function to determine the range of all the
|
||||
var calculateYRange = function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
var positiveValues = [];
|
||||
var negativeValues = [];
|
||||
|
||||
if (self.options.stacked) {
|
||||
self.eachValue(function(value, index) {
|
||||
values[index] = values[index] || 0;
|
||||
negativeValues[index] = negativeValues[index] || 0;
|
||||
if (self.options.relativeBars) {
|
||||
values[index] = 100;
|
||||
} else {
|
||||
if (value < 0) {
|
||||
negativeValues[index] += value;
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
positiveValues[index] = positiveValues[index] || 0;
|
||||
negativeValues[index] = negativeValues[index] || 0;
|
||||
|
||||
if (self.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);
|
||||
} else {
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
// Map of scale ID to scale object so we can lookup later
|
||||
this.scales = {};
|
||||
|
||||
// Build the x axis. The line chart only supports a single x axis
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(this.options.scales.xAxes[0].scaleType);
|
||||
var xScale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: this.options.scales.xAxes[0],
|
||||
id: this.options.scales.xAxes[0].id,
|
||||
calculateRange: function() {
|
||||
this.labels = self.data.labels;
|
||||
this.min = 0;
|
||||
this.max = this.labels.length;
|
||||
},
|
||||
calculateBaseWidth: function() {
|
||||
return (this.getPixelForValue(null, 1, true) - this.getPixelForValue(null, 0, true)) - (2 * self.options.barValueSpacing);
|
||||
},
|
||||
calculateBarWidth: function(datasetCount) {
|
||||
//The padding between datasets is to the right of each bar, providing that there are more than 1 dataset
|
||||
var baseWidth = this.calculateBaseWidth() - ((datasetCount - 1) * self.options.barDatasetSpacing);
|
||||
|
||||
if (self.options.stacked) {
|
||||
return baseWidth;
|
||||
}
|
||||
return (baseWidth / datasetCount);
|
||||
},
|
||||
calculateBarX: function(datasetCount, datasetIndex, elementIndex) {
|
||||
var xWidth = this.calculateBaseWidth(),
|
||||
xAbsolute = this.getPixelForValue(null, elementIndex, true) - (xWidth / 2),
|
||||
barWidth = this.calculateBarWidth(datasetCount);
|
||||
|
||||
if (self.options.stacked) {
|
||||
return xAbsolute + barWidth / 2;
|
||||
}
|
||||
|
||||
return xAbsolute + (barWidth * datasetIndex) + (datasetIndex * self.options.barDatasetSpacing) + barWidth / 2;
|
||||
},
|
||||
});
|
||||
this.scales[xScale.id] = xScale;
|
||||
|
||||
// Build up all the y scales
|
||||
helpers.each(this.options.scales.yAxes, function(yAxisOptions) {
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(yAxisOptions.scaleType);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: yAxisOptions,
|
||||
calculateRange: calculateYRange,
|
||||
calculateBarBase: function(datasetIndex, index) {
|
||||
var base = 0;
|
||||
|
||||
if (self.options.stacked) {
|
||||
var bar = self.data.datasets[datasetIndex].metaData[index];
|
||||
|
||||
if (bar.value < 0) {
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (self.data.datasets[i].yAxisID === this.id) {
|
||||
base += self.data.datasets[i].metaData[index].value < base ? self.data.datasets[i].metaData[index].value : 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
values[index] += value;
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (self.data.datasets[i].yAxisID === this.id) {
|
||||
base += self.data.datasets[i].metaData[index].value > base ? self.data.datasets[i].metaData[index].value : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.getPixelForValue(base);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
return base;
|
||||
|
||||
},
|
||||
calculateBarY: function(datasets, datasetIndex, barIndex, value) {
|
||||
|
||||
if (self.options.stacked) {
|
||||
|
||||
var sumPos = 0,
|
||||
sumNeg = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; i++) {
|
||||
if (datasets[i].metaData[barIndex].value < 0) {
|
||||
sumNeg += datasets[i].metaData[barIndex].value || 0;
|
||||
} else {
|
||||
sumPos += datasets[i].metaData[barIndex].value || 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return this.getPixelForValue(sumNeg + value);
|
||||
} else {
|
||||
return this.getPixelForValue(sumPos + value);
|
||||
}
|
||||
|
||||
/*if (options.relativeBars) {
|
||||
offset = offset / sum * 100;
|
||||
}*/
|
||||
|
||||
return this.getPixelForValue(0);
|
||||
}
|
||||
|
||||
var offset = 0;
|
||||
|
||||
for (i = datasetIndex; i < datasets.length; i++) {
|
||||
if (i === datasetIndex && value) {
|
||||
offset += value;
|
||||
} else {
|
||||
offset = offset + (datasets[i].metaData[barIndex].value);
|
||||
}
|
||||
}
|
||||
});
|
||||
return values.concat(negativeValues);
|
||||
}
|
||||
|
||||
self.eachValue(function(value, index) {
|
||||
values.push(value);
|
||||
return this.getPixelForValue(value);
|
||||
},
|
||||
|
||||
calculateBaseHeight: function() {
|
||||
return (this.getPixelForValue(1) - this.getPixelForValue(0));
|
||||
},
|
||||
id: yAxisOptions.id,
|
||||
});
|
||||
|
||||
return values;
|
||||
|
||||
};
|
||||
|
||||
var scaleOptions = {
|
||||
templateString: this.options.scaleLabel,
|
||||
height: this.chart.height,
|
||||
width: this.chart.width,
|
||||
ctx: this.chart.ctx,
|
||||
textColor: this.options.scaleFontColor,
|
||||
fontSize: this.options.scaleFontSize,
|
||||
fontStyle: this.options.scaleFontStyle,
|
||||
fontFamily: this.options.scaleFontFamily,
|
||||
valuesCount: labels.length,
|
||||
beginAtZero: this.options.scaleBeginAtZero,
|
||||
integersOnly: this.options.scaleIntegersOnly,
|
||||
calculateYRange: function(currentHeight) {
|
||||
var updatedRanges = helpers.calculateScaleRange(
|
||||
dataTotal(),
|
||||
currentHeight,
|
||||
this.fontSize,
|
||||
this.beginAtZero,
|
||||
this.integersOnly
|
||||
);
|
||||
helpers.extend(this, updatedRanges);
|
||||
},
|
||||
xLabels: labels,
|
||||
font: helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
|
||||
lineWidth: this.options.scaleLineWidth,
|
||||
lineColor: this.options.scaleLineColor,
|
||||
showHorizontalLines: this.options.scaleShowHorizontalLines,
|
||||
showVerticalLines: this.options.scaleShowVerticalLines,
|
||||
gridLineWidth: (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
|
||||
gridLineColor: (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
|
||||
padding: (this.options.showScale) ? 0 : this.options.borderWidth,
|
||||
showLabels: this.options.scaleShowLabels,
|
||||
display: this.options.showScale
|
||||
};
|
||||
|
||||
if (this.options.scaleOverride) {
|
||||
helpers.extend(scaleOptions, {
|
||||
calculateYRange: helpers.noop,
|
||||
steps: this.options.scaleSteps,
|
||||
stepValue: this.options.scaleStepWidth,
|
||||
min: this.options.scaleStartValue,
|
||||
max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
|
||||
});
|
||||
}
|
||||
|
||||
this.scale = new this.ScaleClass(scaleOptions);
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
},
|
||||
// This should be incorportated into the init as something like a default value. "Reflow" seems like a weird word for a fredraw function
|
||||
redraw: function() {
|
||||
var base = this.scale.zeroPoint;
|
||||
this.eachElement(function(element, index, datasetIndex) {
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
var base = yScale.getPixelForValue(yScale.min);
|
||||
|
||||
if (yScale.min <= 0 && yScale.max >= 0) {
|
||||
// have a 0 point
|
||||
base = yScale.getPixelForValue(0);
|
||||
} else if (yScale.min < 0 && yScale.max < 0) {
|
||||
// all megative
|
||||
base = yScale.getPixelForValue(yScale.max);
|
||||
}
|
||||
|
||||
helpers.extend(element, {
|
||||
y: base,
|
||||
base: base
|
||||
@ -445,7 +519,10 @@
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
this.scale.draw(easingDecimal);
|
||||
// Draw all the scales
|
||||
helpers.each(this.scales, function(scale) {
|
||||
scale.draw(this.chartArea);
|
||||
}, this);
|
||||
|
||||
//Draw all the bars for each dataset
|
||||
this.eachElement(function(bar, index, datasetIndex) {
|
||||
|
||||
@ -242,6 +242,37 @@
|
||||
args.unshift({});
|
||||
return extend.apply(null, args);
|
||||
},
|
||||
// Need a special merge function to chart configs since they are now grouped
|
||||
configMerge = helpers.configMerge = function(base) {
|
||||
helpers.each(Array.prototype.slice.call(arguments, 1), function(extension) {
|
||||
helpers.each(extension, function(value, key) {
|
||||
if (extension.hasOwnProperty(key)) {
|
||||
if (base.hasOwnProperty(key) && helpers.isArray(base[key]) && helpers.isArray(value)) {
|
||||
// In this case we have an array of objects replacing another array. Rather than doing a strict replace,
|
||||
// merge. This allows easy scale option merging
|
||||
var baseArray = base[key];
|
||||
|
||||
helpers.each(value, function(valueObj, index) {
|
||||
if (index < baseArray.length) {
|
||||
baseArray[index] = helpers.configMerge(baseArray[index], valueObj);
|
||||
} else {
|
||||
baseArray.push(valueObj); // nothing to merge
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (base.hasOwnProperty(key) && typeof base[key] == "object" && typeof value == "object") {
|
||||
// If we are overwriting an object with an object, do a merge of the properties.
|
||||
base[key] = helpers.configMerge(base[key], value);
|
||||
} else {
|
||||
// can just overwrite the value in this case
|
||||
base[key] = value;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return base;
|
||||
},
|
||||
indexOf = helpers.indexOf = function(arrayToSearch, item) {
|
||||
if (Array.prototype.indexOf) {
|
||||
return arrayToSearch.indexOf(item);
|
||||
@ -330,6 +361,17 @@
|
||||
min = helpers.min = function(array) {
|
||||
return Math.min.apply(Math, array);
|
||||
},
|
||||
sign = helpers.sign = function(x) {
|
||||
if (Math.sign) {
|
||||
return Math.sign(x);
|
||||
} else {
|
||||
x = +x; // convert to a number
|
||||
if (x === 0 || isNaN(x)) {
|
||||
return x;
|
||||
}
|
||||
return x > 0 ? 1 : -1;
|
||||
}
|
||||
},
|
||||
cap = helpers.cap = function(valueToCap, maxValue, minValue) {
|
||||
if (isNumber(maxValue)) {
|
||||
if (valueToCap > maxValue) {
|
||||
@ -360,9 +402,12 @@
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
toRadians = helpers.radians = function(degrees) {
|
||||
toRadians = helpers.toRadians = function(degrees) {
|
||||
return degrees * (Math.PI / 180);
|
||||
},
|
||||
toDegrees = helpers.toDegrees = function(radians) {
|
||||
return radians * (180 / Math.PI);
|
||||
},
|
||||
// Gets the angle from vertical upright to the point about a centre.
|
||||
getAngleFromPoint = helpers.getAngleFromPoint = function(centrePoint, anglePoint) {
|
||||
var distanceFromXCenter = anglePoint.x - centrePoint.x,
|
||||
@ -482,6 +527,36 @@
|
||||
};
|
||||
|
||||
},
|
||||
// Implementation of the nice number algorithm used in determining where axis labels will go
|
||||
niceNum = helpers.niceNum = function(range, round) {
|
||||
var exponent = Math.floor(Math.log10(range));
|
||||
var fraction = range / Math.pow(10, exponent);
|
||||
var niceFraction;
|
||||
|
||||
if (round) {
|
||||
if (fraction < 1.5) {
|
||||
niceFraction = 1;
|
||||
} else if (fraction < 3) {
|
||||
niceFraction = 2;
|
||||
} else if (fraction < 7) {
|
||||
niceFraction = 5;
|
||||
} else {
|
||||
niceFraction = 10;
|
||||
}
|
||||
} else {
|
||||
if (fraction <= 1.0) {
|
||||
niceFraction = 1;
|
||||
} else if (fraction <= 2) {
|
||||
niceFraction = 2;
|
||||
} else if (fraction <= 5) {
|
||||
niceFraction = 5;
|
||||
} else {
|
||||
niceFraction = 10;
|
||||
}
|
||||
}
|
||||
|
||||
return niceFraction * Math.pow(10, exponent);
|
||||
},
|
||||
/* jshint ignore:start */
|
||||
// Blows up jshint errors based on the new Function constructor
|
||||
//Templating methods
|
||||
@ -1086,13 +1161,13 @@
|
||||
|
||||
var baseDefaults = (Chart.defaults[parent.prototype.name]) ? clone(Chart.defaults[parent.prototype.name]) : {};
|
||||
|
||||
Chart.defaults[chartName] = extend(baseDefaults, extensions.defaults);
|
||||
Chart.defaults[chartName] = helpers.configMerge(baseDefaults, extensions.defaults);
|
||||
|
||||
Chart.types[chartName] = ChartType;
|
||||
|
||||
//Register this new chart type in the Chart prototype
|
||||
Chart.prototype[chartName] = function(config) {
|
||||
helpers.extend(config.options, merge(Chart.defaults.global, Chart.defaults[chartName], config.options || {}));
|
||||
config.options = helpers.configMerge(Chart.defaults.global, Chart.defaults[chartName], config.options || {});
|
||||
return new ChartType(config, this);
|
||||
};
|
||||
} else {
|
||||
@ -1332,7 +1407,7 @@
|
||||
},
|
||||
draw: function() {
|
||||
|
||||
var ctx = this._chart.ctx;
|
||||
var ctx = this.ctx;
|
||||
var vm = this._vm;
|
||||
|
||||
ctx.beginPath();
|
||||
@ -1707,542 +1782,6 @@
|
||||
},
|
||||
});
|
||||
|
||||
Chart.Scale = Chart.Element.extend({
|
||||
initialize: function() {
|
||||
this.fit();
|
||||
},
|
||||
buildYLabels: function() {
|
||||
this.yLabels = [];
|
||||
|
||||
var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
|
||||
|
||||
for (var i = 0; i <= this.steps; i++) {
|
||||
this.yLabels.push(template(this.templateString, {
|
||||
value: (this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)
|
||||
}));
|
||||
}
|
||||
this.yLabelWidth = (this.display && this.showLabels) ? longestText(this.ctx, this.font, this.yLabels) + 10 : 0;
|
||||
},
|
||||
addXLabel: function(label) {
|
||||
this.xLabels.push(label);
|
||||
this.valuesCount++;
|
||||
this.fit();
|
||||
},
|
||||
removeXLabel: function() {
|
||||
this.xLabels.shift();
|
||||
this.valuesCount--;
|
||||
this.fit();
|
||||
},
|
||||
// Fitting loop to rotate x Labels and figure out what fits there, and also calculate how many Y steps to use
|
||||
fit: function() {
|
||||
// First we need the width of the yLabels, assuming the xLabels aren't rotated
|
||||
|
||||
// To do that we need the base line at the top and base of the chart, assuming there is no x label rotation
|
||||
this.startPoint = (this.display) ? this.fontSize : 0;
|
||||
this.endPoint = (this.display) ? this.height - (this.fontSize * 1.5) - 5 : this.height; // -5 to pad labels
|
||||
|
||||
// Apply padding settings to the start and end point.
|
||||
this.startPoint += this.padding;
|
||||
this.endPoint -= this.padding;
|
||||
|
||||
// Cache the starting endpoint, excluding the space for x labels
|
||||
var cachedEndPoint = this.endPoint;
|
||||
|
||||
// Cache the starting height, so can determine if we need to recalculate the scale yAxis
|
||||
var cachedHeight = this.endPoint - this.startPoint,
|
||||
cachedYLabelWidth;
|
||||
|
||||
// Build the current yLabels so we have an idea of what size they'll be to start
|
||||
/*
|
||||
* This sets what is returned from calculateScaleRange as static properties of this class:
|
||||
*
|
||||
this.steps;
|
||||
this.stepValue;
|
||||
this.min;
|
||||
this.max;
|
||||
*
|
||||
*/
|
||||
this.calculateYRange(cachedHeight);
|
||||
|
||||
// With these properties set we can now build the array of yLabels
|
||||
// and also the width of the largest yLabel
|
||||
this.buildYLabels();
|
||||
|
||||
this.calculateXLabelRotation();
|
||||
|
||||
while ((cachedHeight > this.endPoint - this.startPoint)) {
|
||||
cachedHeight = this.endPoint - this.startPoint;
|
||||
cachedYLabelWidth = this.yLabelWidth;
|
||||
|
||||
this.calculateYRange(cachedHeight);
|
||||
this.buildYLabels();
|
||||
|
||||
// Only go through the xLabel loop again if the yLabel width has changed
|
||||
if (cachedYLabelWidth < this.yLabelWidth) {
|
||||
this.endPoint = cachedEndPoint;
|
||||
this.calculateXLabelRotation();
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
calculateXLabelRotation: function() {
|
||||
//Get the width of each grid by calculating the difference
|
||||
//between x offsets between 0 and 1.
|
||||
|
||||
this.ctx.font = this.font;
|
||||
|
||||
var firstWidth = this.ctx.measureText(this.xLabels[0]).width,
|
||||
lastWidth = this.ctx.measureText(this.xLabels[this.xLabels.length - 1]).width,
|
||||
firstRotated,
|
||||
lastRotated;
|
||||
|
||||
|
||||
this.xScalePaddingRight = lastWidth / 2 + 3;
|
||||
this.xScalePaddingLeft = (firstWidth / 2 > this.yLabelWidth) ? firstWidth / 2 : this.yLabelWidth;
|
||||
|
||||
this.xLabelRotation = 0;
|
||||
if (this.display) {
|
||||
var originalLabelWidth = longestText(this.ctx, this.font, this.xLabels),
|
||||
cosRotation,
|
||||
firstRotatedWidth;
|
||||
this.xLabelWidth = originalLabelWidth;
|
||||
//Allow 3 pixels x2 padding either side for label readability
|
||||
var xGridWidth = Math.floor(this.calculateX(1) - this.calculateX(0)) - 6;
|
||||
|
||||
//Max label rotate should be 90 - also act as a loop counter
|
||||
while ((this.xLabelWidth > xGridWidth && this.xLabelRotation === 0) || (this.xLabelWidth > xGridWidth && this.xLabelRotation <= 90 && this.xLabelRotation > 0)) {
|
||||
cosRotation = Math.cos(toRadians(this.xLabelRotation));
|
||||
|
||||
firstRotated = cosRotation * firstWidth;
|
||||
lastRotated = cosRotation * lastWidth;
|
||||
|
||||
// We're right aligning the text now.
|
||||
if (firstRotated + this.fontSize / 2 > this.yLabelWidth) {
|
||||
this.xScalePaddingLeft = firstRotated + this.fontSize / 2;
|
||||
}
|
||||
this.xScalePaddingRight = this.fontSize / 2;
|
||||
|
||||
|
||||
this.xLabelRotation++;
|
||||
this.xLabelWidth = cosRotation * originalLabelWidth;
|
||||
|
||||
}
|
||||
if (this.xLabelRotation > 0) {
|
||||
this.endPoint -= Math.sin(toRadians(this.xLabelRotation)) * originalLabelWidth + 3;
|
||||
}
|
||||
} else {
|
||||
this.xLabelWidth = 0;
|
||||
this.xScalePaddingRight = this.padding;
|
||||
this.xScalePaddingLeft = this.padding;
|
||||
}
|
||||
|
||||
},
|
||||
// Needs to be overidden in each Chart type
|
||||
// Otherwise we need to pass all the data into the scale class
|
||||
calculateYRange: noop,
|
||||
drawingArea: function() {
|
||||
return this.startPoint - this.endPoint;
|
||||
},
|
||||
calculateY: function(value) {
|
||||
var scalingFactor = this.drawingArea() / (this.min - this.max);
|
||||
return this.endPoint - (scalingFactor * (value - this.min));
|
||||
},
|
||||
calculateX: function(index) {
|
||||
var isRotated = (this.xLabelRotation > 0),
|
||||
// innerWidth = (this.offsetGridLines) ? this.width - offsetLeft - this.padding : this.width - (offsetLeft + halfLabelWidth * 2) - this.padding,
|
||||
innerWidth = this.width - (this.xScalePaddingLeft + this.xScalePaddingRight),
|
||||
valueWidth = innerWidth / Math.max((this.valuesCount - ((this.offsetGridLines) ? 0 : 1)), 1),
|
||||
valueOffset = (valueWidth * index) + this.xScalePaddingLeft;
|
||||
|
||||
if (this.offsetGridLines) {
|
||||
valueOffset += (valueWidth / 2);
|
||||
}
|
||||
|
||||
return Math.round(valueOffset);
|
||||
},
|
||||
update: function(newProps) {
|
||||
helpers.extend(this, newProps);
|
||||
this.fit();
|
||||
},
|
||||
draw: function() {
|
||||
var ctx = this.ctx,
|
||||
yLabelGap = (this.endPoint - this.startPoint) / this.steps,
|
||||
xStart = Math.round(this.xScalePaddingLeft);
|
||||
if (this.display) {
|
||||
ctx.fillStyle = this.textColor;
|
||||
ctx.font = this.font;
|
||||
each(this.yLabels, function(labelString, index) {
|
||||
var yLabelCenter = this.endPoint - (yLabelGap * index),
|
||||
linePositionY = Math.round(yLabelCenter),
|
||||
drawHorizontalLine = this.showHorizontalLines;
|
||||
|
||||
ctx.textAlign = "right";
|
||||
ctx.textBaseline = "middle";
|
||||
if (this.showLabels) {
|
||||
ctx.fillText(labelString, xStart - 10, yLabelCenter);
|
||||
}
|
||||
|
||||
// This is X axis, so draw it
|
||||
if (index === 0 && !drawHorizontalLine) {
|
||||
drawHorizontalLine = true;
|
||||
}
|
||||
|
||||
if (drawHorizontalLine) {
|
||||
ctx.beginPath();
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
// This is a grid line in the centre, so drop that
|
||||
ctx.lineWidth = this.gridLineWidth;
|
||||
ctx.strokeStyle = this.gridLineColor;
|
||||
} else {
|
||||
// This is the first line on the scale
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
ctx.strokeStyle = this.lineColor;
|
||||
}
|
||||
|
||||
linePositionY += helpers.aliasPixel(ctx.lineWidth);
|
||||
|
||||
if (drawHorizontalLine) {
|
||||
ctx.moveTo(xStart, linePositionY);
|
||||
ctx.lineTo(this.width, linePositionY);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
ctx.strokeStyle = this.lineColor;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(xStart - 5, linePositionY);
|
||||
ctx.lineTo(xStart, linePositionY);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
|
||||
}, this);
|
||||
|
||||
each(this.xLabels, function(label, index) {
|
||||
var xPos = this.calculateX(index) + aliasPixel(this.lineWidth),
|
||||
// Check to see if line/bar here and decide where to place the line
|
||||
linePos = this.calculateX(index - (this.offsetGridLines ? 0.5 : 0)) + aliasPixel(this.lineWidth),
|
||||
isRotated = (this.xLabelRotation > 0),
|
||||
drawVerticalLine = this.showVerticalLines;
|
||||
|
||||
// This is Y axis, so draw it
|
||||
if (index === 0 && !drawVerticalLine) {
|
||||
drawVerticalLine = true;
|
||||
}
|
||||
|
||||
if (drawVerticalLine) {
|
||||
ctx.beginPath();
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
// This is a grid line in the centre, so drop that
|
||||
ctx.lineWidth = this.gridLineWidth;
|
||||
ctx.strokeStyle = this.gridLineColor;
|
||||
} else {
|
||||
// This is the first line on the scale
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
ctx.strokeStyle = this.lineColor;
|
||||
}
|
||||
|
||||
if (drawVerticalLine) {
|
||||
ctx.moveTo(linePos, this.endPoint);
|
||||
ctx.lineTo(linePos, this.startPoint - 3);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
ctx.strokeStyle = this.lineColor;
|
||||
|
||||
|
||||
// Small lines at the bottom of the base grid line
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(linePos, this.endPoint);
|
||||
ctx.lineTo(linePos, this.endPoint + 5);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
|
||||
ctx.save();
|
||||
ctx.translate(xPos, (isRotated) ? this.endPoint + 12 : this.endPoint + 8);
|
||||
ctx.rotate(toRadians(this.xLabelRotation) * -1);
|
||||
ctx.font = this.font;
|
||||
ctx.textAlign = (isRotated) ? "right" : "center";
|
||||
ctx.textBaseline = (isRotated) ? "middle" : "top";
|
||||
ctx.fillText(label, 0, 0);
|
||||
ctx.restore();
|
||||
}, this);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Chart.RadialScale = Chart.Element.extend({
|
||||
initialize: function() {
|
||||
this.size = min([this.height, this.width]);
|
||||
this.drawingArea = (this.display) ? (this.size / 2) - (this.fontSize / 2 + this.backdropPaddingY) : (this.size / 2);
|
||||
},
|
||||
calculateCenterOffset: function(value) {
|
||||
// Take into account half font size + the yPadding of the top value
|
||||
var scalingFactor = this.drawingArea / (this.max - this.min);
|
||||
|
||||
return (value - this.min) * scalingFactor;
|
||||
},
|
||||
update: function() {
|
||||
if (!this.lineArc) {
|
||||
this.setScaleSize();
|
||||
} else {
|
||||
this.drawingArea = (this.display) ? (this.size / 2) - (this.fontSize / 2 + this.backdropPaddingY) : (this.size / 2);
|
||||
}
|
||||
this.buildYLabels();
|
||||
},
|
||||
buildYLabels: function() {
|
||||
this.yLabels = [];
|
||||
|
||||
var stepDecimalPlaces = getDecimalPlaces(this.stepValue);
|
||||
|
||||
for (var i = 0; i <= this.steps; i++) {
|
||||
this.yLabels.push(template(this.templateString, {
|
||||
value: (this.min + (i * this.stepValue)).toFixed(stepDecimalPlaces)
|
||||
}));
|
||||
}
|
||||
},
|
||||
getCircumference: function() {
|
||||
return ((Math.PI * 2) / this.valuesCount);
|
||||
},
|
||||
setScaleSize: function() {
|
||||
/*
|
||||
* Right, this is really confusing and there is a lot of maths going on here
|
||||
* The gist of the problem is here: https://gist.github.com/nnnick/696cc9c55f4b0beb8fe9
|
||||
*
|
||||
* Reaction: https://dl.dropboxusercontent.com/u/34601363/toomuchscience.gif
|
||||
*
|
||||
* Solution:
|
||||
*
|
||||
* We assume the radius of the polygon is half the size of the canvas at first
|
||||
* at each index we check if the text overlaps.
|
||||
*
|
||||
* Where it does, we store that angle and that index.
|
||||
*
|
||||
* After finding the largest index and angle we calculate how much we need to remove
|
||||
* from the shape radius to move the point inwards by that x.
|
||||
*
|
||||
* We average the left and right distances to get the maximum shape radius that can fit in the box
|
||||
* along with labels.
|
||||
*
|
||||
* Once we have that, we can find the centre point for the chart, by taking the x text protrusion
|
||||
* on each side, removing that from the size, halving it and adding the left x protrusion width.
|
||||
*
|
||||
* This will mean we have a shape fitted to the canvas, as large as it can be with the labels
|
||||
* and position it in the most space efficient manner
|
||||
*
|
||||
* https://dl.dropboxusercontent.com/u/34601363/yeahscience.gif
|
||||
*/
|
||||
|
||||
|
||||
// Get maximum radius of the polygon. Either half the height (minus the text width) or half the width.
|
||||
// Use this to calculate the offset + change. - Make sure L/R protrusion is at least 0 to stop issues with centre points
|
||||
var largestPossibleRadius = min([(this.height / 2 - this.pointLabelFontSize - 5), this.width / 2]),
|
||||
pointPosition,
|
||||
i,
|
||||
textWidth,
|
||||
halfTextWidth,
|
||||
furthestRight = this.width,
|
||||
furthestRightIndex,
|
||||
furthestRightAngle,
|
||||
furthestLeft = 0,
|
||||
furthestLeftIndex,
|
||||
furthestLeftAngle,
|
||||
xProtrusionLeft,
|
||||
xProtrusionRight,
|
||||
radiusReductionRight,
|
||||
radiusReductionLeft,
|
||||
maxWidthRadius;
|
||||
this.ctx.font = fontString(this.pointLabelFontSize, this.pointLabelFontStyle, this.pointLabelFontFamily);
|
||||
for (i = 0; i < this.valuesCount; i++) {
|
||||
// 5px to space the text slightly out - similar to what we do in the draw function.
|
||||
pointPosition = this.getPointPosition(i, largestPossibleRadius);
|
||||
textWidth = this.ctx.measureText(template(this.templateString, {
|
||||
value: this.labels[i]
|
||||
})).width + 5;
|
||||
if (i === 0 || i === this.valuesCount / 2) {
|
||||
// If we're at index zero, or exactly the middle, we're at exactly the top/bottom
|
||||
// of the radar chart, so text will be aligned centrally, so we'll half it and compare
|
||||
// w/left and right text sizes
|
||||
halfTextWidth = textWidth / 2;
|
||||
if (pointPosition.x + halfTextWidth > furthestRight) {
|
||||
furthestRight = pointPosition.x + halfTextWidth;
|
||||
furthestRightIndex = i;
|
||||
}
|
||||
if (pointPosition.x - halfTextWidth < furthestLeft) {
|
||||
furthestLeft = pointPosition.x - halfTextWidth;
|
||||
furthestLeftIndex = i;
|
||||
}
|
||||
} else if (i < this.valuesCount / 2) {
|
||||
// Less than half the values means we'll left align the text
|
||||
if (pointPosition.x + textWidth > furthestRight) {
|
||||
furthestRight = pointPosition.x + textWidth;
|
||||
furthestRightIndex = i;
|
||||
}
|
||||
} else if (i > this.valuesCount / 2) {
|
||||
// More than half the values means we'll right align the text
|
||||
if (pointPosition.x - textWidth < furthestLeft) {
|
||||
furthestLeft = pointPosition.x - textWidth;
|
||||
furthestLeftIndex = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xProtrusionLeft = furthestLeft;
|
||||
|
||||
xProtrusionRight = Math.ceil(furthestRight - this.width);
|
||||
|
||||
furthestRightAngle = this.getIndexAngle(furthestRightIndex);
|
||||
|
||||
furthestLeftAngle = this.getIndexAngle(furthestLeftIndex);
|
||||
|
||||
radiusReductionRight = xProtrusionRight / Math.sin(furthestRightAngle + Math.PI / 2);
|
||||
|
||||
radiusReductionLeft = xProtrusionLeft / Math.sin(furthestLeftAngle + Math.PI / 2);
|
||||
|
||||
// Ensure we actually need to reduce the size of the chart
|
||||
radiusReductionRight = (isNumber(radiusReductionRight)) ? radiusReductionRight : 0;
|
||||
radiusReductionLeft = (isNumber(radiusReductionLeft)) ? radiusReductionLeft : 0;
|
||||
|
||||
this.drawingArea = largestPossibleRadius - (radiusReductionLeft + radiusReductionRight) / 2;
|
||||
|
||||
//this.drawingArea = min([maxWidthRadius, (this.height - (2 * (this.pointLabelFontSize + 5)))/2])
|
||||
this.setCenterPoint(radiusReductionLeft, radiusReductionRight);
|
||||
|
||||
},
|
||||
setCenterPoint: function(leftMovement, rightMovement) {
|
||||
|
||||
var maxRight = this.width - rightMovement - this.drawingArea,
|
||||
maxLeft = leftMovement + this.drawingArea;
|
||||
|
||||
this.xCenter = (maxLeft + maxRight) / 2;
|
||||
// Always vertically in the centre as the text height doesn't change
|
||||
this.yCenter = (this.height / 2);
|
||||
},
|
||||
|
||||
getIndexAngle: function(index) {
|
||||
var angleMultiplier = (Math.PI * 2) / this.valuesCount;
|
||||
// Start from the top instead of right, so remove a quarter of the circle
|
||||
|
||||
return index * angleMultiplier - (Math.PI / 2);
|
||||
},
|
||||
getPointPosition: function(index, distanceFromCenter) {
|
||||
var thisAngle = this.getIndexAngle(index);
|
||||
return {
|
||||
x: (Math.cos(thisAngle) * distanceFromCenter) + this.xCenter,
|
||||
y: (Math.sin(thisAngle) * distanceFromCenter) + this.yCenter
|
||||
};
|
||||
},
|
||||
draw: function() {
|
||||
if (this.display) {
|
||||
var ctx = this.ctx;
|
||||
each(this.yLabels, function(label, index) {
|
||||
// Don't draw a centre value
|
||||
if (index > 0) {
|
||||
var yCenterOffset = index * (this.drawingArea / this.steps),
|
||||
yHeight = this.yCenter - yCenterOffset,
|
||||
pointPosition;
|
||||
|
||||
// Draw circular lines around the scale
|
||||
if (this.lineWidth > 0) {
|
||||
ctx.strokeStyle = this.lineColor;
|
||||
ctx.lineWidth = this.lineWidth;
|
||||
|
||||
if (this.lineArc) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(this.xCenter, this.yCenter, yCenterOffset, 0, Math.PI * 2);
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
} else {
|
||||
ctx.beginPath();
|
||||
for (var i = 0; i < this.valuesCount; i++) {
|
||||
pointPosition = this.getPointPosition(i, this.calculateCenterOffset(this.min + (index * this.stepValue)));
|
||||
if (i === 0) {
|
||||
ctx.moveTo(pointPosition.x, pointPosition.y);
|
||||
} else {
|
||||
ctx.lineTo(pointPosition.x, pointPosition.y);
|
||||
}
|
||||
}
|
||||
ctx.closePath();
|
||||
ctx.stroke();
|
||||
}
|
||||
}
|
||||
if (this.showLabels) {
|
||||
ctx.font = fontString(this.fontSize, this._fontStyle, this._fontFamily);
|
||||
if (this.showLabelBackdrop) {
|
||||
var labelWidth = ctx.measureText(label).width;
|
||||
ctx.fillStyle = this.backdropColor;
|
||||
ctx.fillRect(
|
||||
this.xCenter - labelWidth / 2 - this.backdropPaddingX,
|
||||
yHeight - this.fontSize / 2 - this.backdropPaddingY,
|
||||
labelWidth + this.backdropPaddingX * 2,
|
||||
this.fontSize + this.backdropPaddingY * 2
|
||||
);
|
||||
}
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = "middle";
|
||||
ctx.fillStyle = this.fontColor;
|
||||
ctx.fillText(label, this.xCenter, yHeight);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (!this.lineArc) {
|
||||
ctx.lineWidth = this.angleLineWidth;
|
||||
ctx.strokeStyle = this.angleLineColor;
|
||||
for (var i = this.valuesCount - 1; i >= 0; i--) {
|
||||
if (this.angleLineWidth > 0) {
|
||||
var outerPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max));
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(this.xCenter, this.yCenter);
|
||||
ctx.lineTo(outerPosition.x, outerPosition.y);
|
||||
ctx.stroke();
|
||||
ctx.closePath();
|
||||
}
|
||||
// Extra 3px out for some label spacing
|
||||
var pointLabelPosition = this.getPointPosition(i, this.calculateCenterOffset(this.max) + 5);
|
||||
ctx.font = fontString(this.pointLabelFontSize, this.pointLabelFontStyle, this.pointLabelFontFamily);
|
||||
ctx.fillStyle = this.pointLabelFontColor;
|
||||
|
||||
var labelsCount = this.labels.length,
|
||||
halfLabelsCount = this.labels.length / 2,
|
||||
quarterLabelsCount = halfLabelsCount / 2,
|
||||
upperHalf = (i < quarterLabelsCount || i > labelsCount - quarterLabelsCount),
|
||||
exactQuarter = (i === quarterLabelsCount || i === labelsCount - quarterLabelsCount);
|
||||
if (i === 0) {
|
||||
ctx.textAlign = 'center';
|
||||
} else if (i === halfLabelsCount) {
|
||||
ctx.textAlign = 'center';
|
||||
} else if (i < halfLabelsCount) {
|
||||
ctx.textAlign = 'left';
|
||||
} else {
|
||||
ctx.textAlign = 'right';
|
||||
}
|
||||
|
||||
// Set the correct text baseline based on outer positioning
|
||||
if (exactQuarter) {
|
||||
ctx.textBaseline = 'middle';
|
||||
} else if (upperHalf) {
|
||||
ctx.textBaseline = 'bottom';
|
||||
} else {
|
||||
ctx.textBaseline = 'top';
|
||||
}
|
||||
|
||||
ctx.fillText(this.labels[i], pointLabelPosition.x, pointLabelPosition.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Chart.animationService = {
|
||||
frameDuration: 17,
|
||||
animations: [],
|
||||
|
||||
@ -43,11 +43,7 @@
|
||||
defaults: defaultConfig,
|
||||
//Initialize is fired when the chart is initialized - Data is passed in as a parameter
|
||||
//Config is automatically merged by the core of Chart.js, and is available at this.options
|
||||
initialize: function(data) {
|
||||
|
||||
// Save data as a source for updating of values & methods
|
||||
this.data = data;
|
||||
|
||||
initialize: function() {
|
||||
// Slice Type and defaults
|
||||
this.Slice = Chart.Arc.extend({
|
||||
_chart: this.chart,
|
||||
@ -57,7 +53,7 @@
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
if (this.options.showTooltips) {
|
||||
helpers.bindEvents(this, this.options.tooltipEvents, this.onHover);
|
||||
helpers.bindEvents(this, this.options.events, this.onHover);
|
||||
}
|
||||
|
||||
// Create new slice for each piece of data
|
||||
|
||||
@ -7,29 +7,80 @@
|
||||
|
||||
var defaultConfig = {
|
||||
|
||||
///Boolean - Whether grid lines are shown across the chart
|
||||
scaleShowGridLines: true,
|
||||
//String - Colour of the grid lines
|
||||
scaleGridLineColor: "rgba(0,0,0,.05)",
|
||||
//Number - Width of the grid lines
|
||||
scaleGridLineWidth: 1,
|
||||
//Boolean - Whether to show horizontal lines (except X axis)
|
||||
scaleShowHorizontalLines: true,
|
||||
//Boolean - Whether to show vertical lines (except Y axis)
|
||||
scaleShowVerticalLines: true,
|
||||
//Boolean - Whether to horizontally center the label and point dot inside the grid
|
||||
offsetGridLines: false,
|
||||
scales: {
|
||||
xAxes: [{
|
||||
scaleType: "dataset", // scatter should not use a dataset axis
|
||||
display: true,
|
||||
position: "bottom",
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
offsetGridLines: false,
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
},
|
||||
|
||||
//Boolean - Whether to stack the lines essentially creating a stacked area chart.
|
||||
stacked: false,
|
||||
|
||||
|
||||
//Number - Tension of the bezier curve between points
|
||||
tension: 0.4,
|
||||
|
||||
|
||||
//Number - Radius of each point dot in pixels
|
||||
pointRadius: 3,
|
||||
//Number - Pixel width of point dot border
|
||||
@ -51,9 +102,6 @@
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].borderColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>",
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -67,35 +115,6 @@
|
||||
|
||||
var _this = this;
|
||||
|
||||
// Build Scale
|
||||
this.ScaleClass = Chart.Scale.extend({
|
||||
calculatePointY: function(index, datasetIndex) {
|
||||
|
||||
var value = _this.data.datasets[datasetIndex].data[index];
|
||||
|
||||
if (_this.options.stacked) {
|
||||
var offsetPos = 0;
|
||||
var offsetNeg = 0;
|
||||
for (var i = 0; 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.calculateY(offsetNeg + value);
|
||||
} else {
|
||||
return this.calculateY(offsetPos + value);
|
||||
}
|
||||
}
|
||||
|
||||
return this.calculateY(value);
|
||||
}
|
||||
});
|
||||
this.buildScale(this.data.labels);
|
||||
|
||||
|
||||
//Create a new line and its points for each dataset and piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaDataset = new Chart.Line();
|
||||
@ -103,8 +122,19 @@
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new Chart.Point());
|
||||
}, this);
|
||||
|
||||
// The line chart only supports a single x axis because the x axis is always a dataset axis
|
||||
dataset.xAxisID = this.options.scales.xAxes[0].id;
|
||||
|
||||
if (!dataset.yAxisID) {
|
||||
dataset.yAxisID = this.options.scales.yAxes[0].id;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Build and fit the scale. Needs to happen after the axis IDs have been set
|
||||
this.buildScale();
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Set defaults for lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
@ -120,9 +150,11 @@
|
||||
|
||||
// Set defaults for points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
|
||||
helpers.extend(point, {
|
||||
x: this.scale.calculateX(index),
|
||||
y: this.scale.calculateY(0),
|
||||
x: xScale.getPixelForValue(null, index, true),
|
||||
y: this.chartArea.bottom,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart
|
||||
@ -155,21 +187,24 @@
|
||||
return collection[index + 1] || collection[index];
|
||||
},
|
||||
update: function() {
|
||||
|
||||
// Update the scale
|
||||
this.scale.update();
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Update the lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
var yScale = this.scales[dataset.yAxisID];
|
||||
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
// Utility
|
||||
_datasetIndex: datasetIndex,
|
||||
|
||||
// Data
|
||||
_points: dataset.metaData,
|
||||
|
||||
// Geometry
|
||||
scaleTop: this.scale.startPoint,
|
||||
scaleBottom: this.scale.endPoint,
|
||||
scaleZero: this.scale.calculateY(0),
|
||||
scaleTop: yScale.top,
|
||||
scaleBottom: yScale.bottom,
|
||||
scaleZero: yScale.getPixelForValue(0),
|
||||
|
||||
// Appearance
|
||||
tension: dataset.tension || this.options.tension,
|
||||
backgroundColor: dataset.backgroundColor || this.options.backgroundColor,
|
||||
@ -181,24 +216,31 @@
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
helpers.extend(point, {
|
||||
// Utility
|
||||
_chart: this.chart,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
|
||||
// Data
|
||||
label: this.data.labels[index],
|
||||
value: this.data.datasets[datasetIndex].data[index],
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
|
||||
// Geometry
|
||||
offsetGridLines: this.options.offsetGridLines,
|
||||
x: this.scale.calculateX(index),
|
||||
y: this.scale.calculatePointY(index, datasetIndex),
|
||||
x: xScale.getPixelForValue(null, index, true), // value not used in dataset scale, but we want a consistent API between scales
|
||||
y: yScale.getPointPixelForValue(this.data.datasets[datasetIndex].data[index], index, datasetIndex),
|
||||
tension: this.data.datasets[datasetIndex].metaDataset.tension,
|
||||
|
||||
// Appearnce
|
||||
radius: this.data.datasets[datasetIndex].pointRadius || this.options.pointRadius,
|
||||
backgroundColor: this.data.datasets[datasetIndex].pointBackgroundColor || this.options.pointBackgroundColor,
|
||||
borderWidth: this.data.datasets[datasetIndex].pointBorderWidth || this.options.pointBorderWidth,
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: this.data.datasets[datasetIndex].pointHitRadius || this.options.pointHitRadius,
|
||||
});
|
||||
@ -217,20 +259,21 @@
|
||||
point.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.scale.endPoint) {
|
||||
point.controlPointNextY = this.scale.endPoint;
|
||||
} else if (controlPoints.next.y < this.scale.startPoint) {
|
||||
point.controlPointNextY = this.scale.startPoint;
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.scale.endPoint) {
|
||||
point.controlPointPreviousY = this.scale.endPoint;
|
||||
} else if (controlPoints.previous.y < this.scale.startPoint) {
|
||||
point.controlPointPreviousY = this.scale.startPoint;
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
@ -240,85 +283,112 @@
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function(labels) {
|
||||
buildScale: function() {
|
||||
var self = this;
|
||||
|
||||
var dataTotal = function() {
|
||||
var values = [];
|
||||
// Function to determine the range of all the
|
||||
var calculateYRange = function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
var positiveValues = [];
|
||||
var negativeValues = [];
|
||||
|
||||
if (self.options.stacked) {
|
||||
self.eachValue(function(value, index) {
|
||||
values[index] = values[index] || 0;
|
||||
negativeValues[index] = negativeValues[index] || 0;
|
||||
if (self.options.relativePoints) {
|
||||
values[index] = 100;
|
||||
} else {
|
||||
if (value < 0) {
|
||||
negativeValues[index] += value;
|
||||
} else {
|
||||
values[index] += value;
|
||||
}
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
positiveValues[index] = positiveValues[index] || 0;
|
||||
negativeValues[index] = negativeValues[index] || 0;
|
||||
|
||||
if (self.options.relativePoints) {
|
||||
positiveValues[index] = 100;
|
||||
} else {
|
||||
if (value < 0) {
|
||||
negativeValues[index] += value;
|
||||
} else {
|
||||
positiveValues[index] += value;
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
return values.concat(negativeValues);
|
||||
}, this);
|
||||
|
||||
var values = positiveValues.concat(negativeValues);
|
||||
this.min = helpers.min(values);
|
||||
this.max = helpers.max(values);
|
||||
} else {
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
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);
|
||||
}
|
||||
|
||||
self.eachValue(function(value, index) {
|
||||
values.push(value);
|
||||
});
|
||||
|
||||
return values;
|
||||
|
||||
};
|
||||
|
||||
var scaleOptions = {
|
||||
templateString: this.options.scaleLabel,
|
||||
height: this.chart.height,
|
||||
width: this.chart.width,
|
||||
// Map of scale ID to scale object so we can lookup later
|
||||
this.scales = {};
|
||||
|
||||
// Build the x axis. The line chart only supports a single x axis
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(this.options.scales.xAxes[0].scaleType);
|
||||
var xScale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
textColor: this.options.scaleFontColor,
|
||||
offsetGridLines: this.options.offsetGridLines,
|
||||
fontSize: this.options.scaleFontSize,
|
||||
fontStyle: this.options.scaleFontStyle,
|
||||
fontFamily: this.options.scaleFontFamily,
|
||||
valuesCount: labels.length,
|
||||
beginAtZero: this.options.scaleBeginAtZero,
|
||||
integersOnly: this.options.scaleIntegersOnly,
|
||||
calculateYRange: function(currentHeight) {
|
||||
var updatedRanges = helpers.calculateScaleRange(
|
||||
dataTotal(),
|
||||
currentHeight,
|
||||
this.fontSize,
|
||||
this.beginAtZero,
|
||||
this.integersOnly
|
||||
);
|
||||
helpers.extend(this, updatedRanges);
|
||||
options: this.options.scales.xAxes[0],
|
||||
calculateRange: function() {
|
||||
this.labels = self.data.labels;
|
||||
this.min = 0;
|
||||
this.max = this.labels.length;
|
||||
},
|
||||
xLabels: this.data.labels,
|
||||
font: helpers.fontString(this.options.scaleFontSize, this.options.scaleFontStyle, this.options.scaleFontFamily),
|
||||
lineWidth: this.options.scaleLineWidth,
|
||||
lineColor: this.options.scaleLineColor,
|
||||
showHorizontalLines: this.options.scaleShowHorizontalLines,
|
||||
showVerticalLines: this.options.scaleShowVerticalLines,
|
||||
gridLineWidth: (this.options.scaleShowGridLines) ? this.options.scaleGridLineWidth : 0,
|
||||
gridLineColor: (this.options.scaleShowGridLines) ? this.options.scaleGridLineColor : "rgba(0,0,0,0)",
|
||||
padding: (this.options.showScale) ? 0 : this.options.pointRadius + this.options.pointBorderWidth,
|
||||
showLabels: this.options.scaleShowLabels,
|
||||
display: this.options.showScale
|
||||
};
|
||||
id: this.options.scales.xAxes[0].id,
|
||||
});
|
||||
this.scales[xScale.id] = xScale;
|
||||
|
||||
if (this.options.scaleOverride) {
|
||||
helpers.extend(scaleOptions, {
|
||||
calculateYRange: helpers.noop,
|
||||
steps: this.options.scaleSteps,
|
||||
stepValue: this.options.scaleStepWidth,
|
||||
min: this.options.scaleStartValue,
|
||||
max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
|
||||
// Build up all the y scales
|
||||
helpers.each(this.options.scales.yAxes, function(yAxisOptions) {
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(yAxisOptions.scaleType);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: yAxisOptions,
|
||||
calculateRange: calculateYRange,
|
||||
getPointPixelForValue: function(value, index, datasetIndex) {
|
||||
if (self.options.stacked) {
|
||||
var offsetPos = 0;
|
||||
var offsetNeg = 0;
|
||||
|
||||
for (var i = 0; i < datasetIndex; ++i) {
|
||||
if (self.data.datasets[i].data[index] < 0) {
|
||||
offsetNeg += self.data.datasets[i].data[index];
|
||||
} else {
|
||||
offsetPos += self.data.datasets[i].data[index];
|
||||
}
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return this.getPixelForValue(offsetNeg + value);
|
||||
} else {
|
||||
return this.getPixelForValue(offsetPos + value);
|
||||
}
|
||||
} else {
|
||||
return this.getPixelForValue(value);
|
||||
}
|
||||
},
|
||||
id: yAxisOptions.id,
|
||||
});
|
||||
}
|
||||
|
||||
this.scale = new this.ScaleClass(scaleOptions);
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
},
|
||||
redraw: function() {
|
||||
|
||||
@ -328,7 +398,10 @@
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
this.scale.draw(easingDecimal);
|
||||
// Draw all the scales
|
||||
helpers.each(this.scales, function(scale) {
|
||||
scale.draw(this.chartArea);
|
||||
}, this);
|
||||
|
||||
// reverse for-loop for proper stacking
|
||||
for (var i = this.data.datasets.length - 1; i >= 0; i--) {
|
||||
|
||||
@ -7,24 +7,6 @@
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
//Boolean - Show a backdrop to the scale label
|
||||
scaleShowLabelBackdrop : true,
|
||||
|
||||
//String - The colour of the label backdrop
|
||||
scaleBackdropColor : "rgba(255,255,255,0.75)",
|
||||
|
||||
// Boolean - Whether the scale should begin at zero
|
||||
scaleBeginAtZero : true,
|
||||
|
||||
//Number - The backdrop padding above & below the label in pixels
|
||||
scaleBackdropPaddingY : 2,
|
||||
|
||||
//Number - The backdrop padding to the side of the label in pixels
|
||||
scaleBackdropPaddingX : 2,
|
||||
|
||||
//Boolean - Show line for each value in the scale
|
||||
scaleShowLine : true,
|
||||
|
||||
//Boolean - Stroke a line around each segment in the chart
|
||||
segmentShowStroke : true,
|
||||
|
||||
@ -34,6 +16,48 @@
|
||||
//Number - The width of the stroke value in pixels
|
||||
segmentStrokeWidth : 2,
|
||||
|
||||
scale: {
|
||||
scaleType: "radialLinear",
|
||||
display: true,
|
||||
|
||||
//Boolean - Whether to animate scaling the chart from the centre
|
||||
animate : false,
|
||||
|
||||
lineArc: true,
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: true,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
|
||||
//Boolean - Show a backdrop to the scale label
|
||||
showLabelBackdrop : true,
|
||||
|
||||
//String - The colour of the label backdrop
|
||||
backdropColor : "rgba(255,255,255,0.75)",
|
||||
|
||||
//Number - The backdrop padding above & below the label in pixels
|
||||
backdropPaddingY : 2,
|
||||
|
||||
//Number - The backdrop padding to the side of the label in pixels
|
||||
backdropPaddingX : 2,
|
||||
}
|
||||
},
|
||||
|
||||
//Number - Amount of animation steps
|
||||
animationSteps : 100,
|
||||
|
||||
@ -43,9 +67,6 @@
|
||||
//Boolean - Whether to animate the rotation of the chart
|
||||
animateRotate : true,
|
||||
|
||||
//Boolean - Whether to animate scaling the chart from the centre
|
||||
animateScale : false,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate : "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<segments.length; i++){%><li><span style=\"background-color:<%=segments[i].fillColor%>\"></span><%if(segments[i].label){%><%=segments[i].label%><%}%></li><%}%></ul>"
|
||||
};
|
||||
@ -58,10 +79,7 @@
|
||||
defaults : defaultConfig,
|
||||
//Initialize is fired when the chart is initialized - Data is passed in as a parameter
|
||||
//Config is automatically merged by the core of Chart.js, and is available at this.options
|
||||
initialize: function(data){
|
||||
// Save data as a source for updating of values & methods
|
||||
this.data = data;
|
||||
|
||||
initialize: function(){
|
||||
this.segments = [];
|
||||
//Declare segment class as a chart instance specific class, so it can share props for this instance
|
||||
this.SegmentArc = Chart.Arc.extend({
|
||||
@ -73,47 +91,59 @@
|
||||
x : this.chart.width/2,
|
||||
y : this.chart.height/2
|
||||
});
|
||||
this.scale = new Chart.RadialScale({
|
||||
display: this.options.showScale,
|
||||
fontStyle: this.options.scaleFontStyle,
|
||||
fontSize: this.options.scaleFontSize,
|
||||
fontFamily: this.options.scaleFontFamily,
|
||||
fontColor: this.options.scaleFontColor,
|
||||
showLabels: this.options.scaleShowLabels,
|
||||
showLabelBackdrop: this.options.scaleShowLabelBackdrop,
|
||||
backdropColor: this.options.scaleBackdropColor,
|
||||
backdropPaddingY : this.options.scaleBackdropPaddingY,
|
||||
backdropPaddingX: this.options.scaleBackdropPaddingX,
|
||||
lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
|
||||
lineColor: this.options.scaleLineColor,
|
||||
|
||||
var self = this;
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(this.options.scale.scaleType);
|
||||
this.scale = new ScaleClass({
|
||||
options: this.options.scale,
|
||||
lineArc: true,
|
||||
width: this.chart.width,
|
||||
height: this.chart.height,
|
||||
xCenter: this.chart.width/2,
|
||||
yCenter: this.chart.height/2,
|
||||
ctx : this.chart.ctx,
|
||||
templateString: this.options.scaleLabel,
|
||||
valuesCount: data.length
|
||||
valuesCount: this.data.length,
|
||||
calculateRange: function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(self.data, function(data) {
|
||||
if (this.min === null) {
|
||||
this.min = data.value;
|
||||
} else if (data.value < this.min) {
|
||||
this.min = data.value;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = data.value;
|
||||
} else if (data.value > this.max) {
|
||||
this.max = data.value;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
});
|
||||
|
||||
this.updateScaleRange(data);
|
||||
this.updateScaleRange();
|
||||
this.scale.calculateRange();
|
||||
this.scale.generateTicks();
|
||||
this.scale.buildYLabels();
|
||||
|
||||
this.scale.update();
|
||||
|
||||
helpers.each(data,function(segment,index){
|
||||
helpers.each(this.data,function(segment,index){
|
||||
this.addData(segment,index,true);
|
||||
},this);
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
if (this.options.showTooltips){
|
||||
helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
|
||||
helpers.bindEvents(this, this.options.events, function(evt){
|
||||
var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
|
||||
helpers.each(this.segments,function(segment){
|
||||
segment.restore(["fillColor"]);
|
||||
});
|
||||
|
||||
helpers.each(activeSegments,function(activeSegment){
|
||||
activeSegment.fillColor = activeSegment.highlightColor;
|
||||
});
|
||||
|
||||
this.showTooltip(activeSegments);
|
||||
});
|
||||
}
|
||||
@ -122,12 +152,12 @@
|
||||
},
|
||||
getSegmentsAtEvent : function(e){
|
||||
var segmentsArray = [];
|
||||
|
||||
var location = helpers.getRelativePosition(e);
|
||||
|
||||
helpers.each(this.segments,function(segment){
|
||||
if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
|
||||
},this);
|
||||
|
||||
return segmentsArray;
|
||||
},
|
||||
addData : function(segment, atIndex, silent){
|
||||
@ -160,37 +190,13 @@
|
||||
},this);
|
||||
this.scale.valuesCount = this.segments.length;
|
||||
},
|
||||
updateScaleRange: function(datapoints){
|
||||
var valuesArray = [];
|
||||
helpers.each(datapoints,function(segment){
|
||||
valuesArray.push(segment.value);
|
||||
updateScaleRange: function(){
|
||||
helpers.extend(this.scale, {
|
||||
size: helpers.min([this.chart.width, this.chart.height]),
|
||||
xCenter: this.chart.width/2,
|
||||
yCenter: this.chart.height/2
|
||||
});
|
||||
|
||||
var scaleSizes = (this.options.scaleOverride) ?
|
||||
{
|
||||
steps: this.options.scaleSteps,
|
||||
stepValue: this.options.scaleStepWidth,
|
||||
min: this.options.scaleStartValue,
|
||||
max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
|
||||
} :
|
||||
helpers.calculateScaleRange(
|
||||
valuesArray,
|
||||
helpers.min([this.chart.width, this.chart.height])/2,
|
||||
this.options.scaleFontSize,
|
||||
this.options.scaleBeginAtZero,
|
||||
this.options.scaleIntegersOnly
|
||||
);
|
||||
|
||||
helpers.extend(
|
||||
this.scale,
|
||||
scaleSizes,
|
||||
{
|
||||
size: helpers.min([this.chart.width, this.chart.height]),
|
||||
xCenter: this.chart.width/2,
|
||||
yCenter: this.chart.height/2
|
||||
}
|
||||
);
|
||||
|
||||
},
|
||||
update : function(){
|
||||
|
||||
@ -223,8 +229,11 @@
|
||||
x : this.chart.width/2,
|
||||
y : this.chart.height/2
|
||||
});
|
||||
this.updateScaleRange(this.segments);
|
||||
this.scale.update();
|
||||
|
||||
this.updateScaleRange();
|
||||
this.scale.calculateRange();
|
||||
this.scale.generateTicks();
|
||||
this.scale.buildYLabels();
|
||||
|
||||
helpers.extend(this.scale,{
|
||||
xCenter: this.chart.width/2,
|
||||
@ -232,8 +241,11 @@
|
||||
});
|
||||
|
||||
helpers.each(this.segments, function(segment){
|
||||
segment.update({
|
||||
outerRadius : this.scale.calculateCenterOffset(segment.value)
|
||||
//segment.update({
|
||||
// outerRadius : this.scale.calculateCenterOffset(segment.value)
|
||||
//});
|
||||
helpers.extend(segment, {
|
||||
outerRadius: this.scale.calculateCenterOffset(segment.value)
|
||||
});
|
||||
}, this);
|
||||
|
||||
|
||||
@ -10,47 +10,88 @@
|
||||
Chart.Type.extend({
|
||||
name: "Radar",
|
||||
defaults:{
|
||||
//Boolean - Whether to show lines for each scale point
|
||||
scaleShowLine : true,
|
||||
|
||||
//Boolean - Whether we show the angle lines out of the radar
|
||||
angleShowLineOut : true,
|
||||
scale: {
|
||||
scaleType: "radialLinear",
|
||||
display: true,
|
||||
|
||||
//Boolean - Whether to animate scaling the chart from the centre
|
||||
animate : false,
|
||||
|
||||
//Boolean - Whether to show labels on the scale
|
||||
scaleShowLabels : false,
|
||||
lineArc: false,
|
||||
|
||||
// Boolean - Whether the scale should begin at zero
|
||||
scaleBeginAtZero : true,
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
},
|
||||
|
||||
//String - Colour of the angle line
|
||||
angleLineColor : "rgba(0,0,0,.1)",
|
||||
angleLines: {
|
||||
show: true,
|
||||
color: "rgba(0,0,0,.1)",
|
||||
lineWidth: 1
|
||||
},
|
||||
|
||||
//Number - Pixel width of the angle line
|
||||
angleLineWidth : 1,
|
||||
// scale numbers
|
||||
beginAtZero: true,
|
||||
|
||||
//String - Point label font declaration
|
||||
pointLabelFontFamily : "'Arial'",
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
|
||||
//String - Point label font weight
|
||||
pointLabelFontStyle : "normal",
|
||||
//Boolean - Show a backdrop to the scale label
|
||||
showLabelBackdrop : true,
|
||||
|
||||
//Number - Point label font size in pixels
|
||||
pointLabelFontSize : 10,
|
||||
//String - The colour of the label backdrop
|
||||
backdropColor : "rgba(255,255,255,0.75)",
|
||||
|
||||
//String - Point label font colour
|
||||
pointLabelFontColor : "#666",
|
||||
//Number - The backdrop padding above & below the label in pixels
|
||||
backdropPaddingY : 2,
|
||||
|
||||
//Number - The backdrop padding to the side of the label in pixels
|
||||
backdropPaddingX : 2,
|
||||
},
|
||||
|
||||
pointLabels: {
|
||||
//String - Point label font declaration
|
||||
fontFamily : "'Arial'",
|
||||
|
||||
//String - Point label font weight
|
||||
fontStyle : "normal",
|
||||
|
||||
//Number - Point label font size in pixels
|
||||
fontSize : 10,
|
||||
|
||||
//String - Point label font colour
|
||||
fontColor : "#666",
|
||||
},
|
||||
},
|
||||
|
||||
//Boolean - Whether to show a dot for each point
|
||||
pointDot : true,
|
||||
|
||||
//Number - Radius of each point dot in pixels
|
||||
pointDotRadius : 3,
|
||||
pointRadius: 3,
|
||||
|
||||
//Number - Pixel width of point dot stroke
|
||||
pointDotStrokeWidth : 1,
|
||||
//Number - Pixel width of point dot border
|
||||
pointBorderWidth: 1,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
|
||||
pointHitDetectionRadius : 20,
|
||||
//Number - Pixel width of point on hover
|
||||
pointHoverRadius: 5,
|
||||
|
||||
//Number - Pixel width of point dot border on hover
|
||||
pointHoverBorderWidth: 2,
|
||||
pointBackgroundColor: Chart.defaults.global.defaultColor,
|
||||
pointBorderColor: Chart.defaults.global.defaultColor,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
|
||||
pointHitRadius: 20,
|
||||
|
||||
//Boolean - Whether to show a stroke for datasets
|
||||
datasetStroke : true,
|
||||
@ -66,25 +107,19 @@
|
||||
|
||||
},
|
||||
|
||||
initialize: function(data){
|
||||
// Save data as a source for updating of values & methods
|
||||
this.data = data;
|
||||
|
||||
initialize: function(){
|
||||
this.PointClass = Chart.Point.extend({
|
||||
strokeWidth : this.options.pointDotStrokeWidth,
|
||||
radius : this.options.pointDotRadius,
|
||||
display: this.options.pointDot,
|
||||
hitDetectionRadius : this.options.pointHitDetectionRadius,
|
||||
ctx : this.chart.ctx
|
||||
_chart: this.chart
|
||||
});
|
||||
|
||||
this.datasets = [];
|
||||
|
||||
this.buildScale(data);
|
||||
this.buildScale(this.data);
|
||||
|
||||
//Set up tooltip events on the chart
|
||||
if (this.options.showTooltips){
|
||||
helpers.bindEvents(this, this.options.tooltipEvents, function(evt){
|
||||
helpers.bindEvents(this, this.options.events, function(evt){
|
||||
var activePointsCollection = (evt.type !== 'mouseout') ? this.getPointsAtEvent(evt) : [];
|
||||
|
||||
this.eachPoints(function(point){
|
||||
@ -100,7 +135,7 @@
|
||||
}
|
||||
|
||||
//Iterate through each of the datasets, and build this into a property of the chart
|
||||
helpers.each(data.datasets,function(dataset){
|
||||
helpers.each(this.data.datasets,function(dataset){
|
||||
|
||||
var datasetObject = {
|
||||
label: dataset.label || null,
|
||||
@ -121,14 +156,22 @@
|
||||
}
|
||||
datasetObject.points.push(new this.PointClass({
|
||||
value : dataPoint,
|
||||
label : data.labels[index],
|
||||
label : this.data.labels[index],
|
||||
datasetLabel: dataset.label,
|
||||
x: (this.options.animation) ? this.scale.xCenter : pointPosition.x,
|
||||
y: (this.options.animation) ? this.scale.yCenter : pointPosition.y,
|
||||
strokeColor : dataset.pointStrokeColor,
|
||||
fillColor : dataset.pointColor,
|
||||
highlightFill : dataset.pointHighlightFill || dataset.pointColor,
|
||||
highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor
|
||||
highlightStroke : dataset.pointHighlightStroke || dataset.pointStrokeColor,
|
||||
|
||||
// Appearance
|
||||
radius: dataset.pointRadius || this.options.pointRadius,
|
||||
backgroundColor: dataset.pointBackgroundColor || this.options.pointBackgroundColor,
|
||||
borderWidth: dataset.pointBorderWidth || this.options.pointBorderWidth,
|
||||
|
||||
// Tooltip
|
||||
hoverRadius: dataset.pointHitRadius || this.options.pointHitRadius,
|
||||
}));
|
||||
},this);
|
||||
|
||||
@ -168,78 +211,47 @@
|
||||
},
|
||||
|
||||
buildScale : function(data){
|
||||
this.scale = new Chart.RadialScale({
|
||||
display: this.options.showScale,
|
||||
fontStyle: this.options.scaleFontStyle,
|
||||
fontSize: this.options.scaleFontSize,
|
||||
fontFamily: this.options.scaleFontFamily,
|
||||
fontColor: this.options.scaleFontColor,
|
||||
showLabels: this.options.scaleShowLabels,
|
||||
showLabelBackdrop: this.options.scaleShowLabelBackdrop,
|
||||
backdropColor: this.options.scaleBackdropColor,
|
||||
backdropPaddingY : this.options.scaleBackdropPaddingY,
|
||||
backdropPaddingX: this.options.scaleBackdropPaddingX,
|
||||
lineWidth: (this.options.scaleShowLine) ? this.options.scaleLineWidth : 0,
|
||||
lineColor: this.options.scaleLineColor,
|
||||
angleLineColor : this.options.angleLineColor,
|
||||
angleLineWidth : (this.options.angleShowLineOut) ? this.options.angleLineWidth : 0,
|
||||
// Point labels at the edge of each line
|
||||
pointLabelFontColor : this.options.pointLabelFontColor,
|
||||
pointLabelFontSize : this.options.pointLabelFontSize,
|
||||
pointLabelFontFamily : this.options.pointLabelFontFamily,
|
||||
pointLabelFontStyle : this.options.pointLabelFontStyle,
|
||||
var self = this;
|
||||
|
||||
var ScaleConstructor = Chart.scales.getScaleConstructor(this.options.scale.scaleType);
|
||||
this.scale = new ScaleConstructor({
|
||||
options: this.options.scale,
|
||||
height : this.chart.height,
|
||||
width: this.chart.width,
|
||||
xCenter: this.chart.width/2,
|
||||
yCenter: this.chart.height/2,
|
||||
ctx : this.chart.ctx,
|
||||
templateString: this.options.scaleLabel,
|
||||
labels: data.labels,
|
||||
valuesCount: data.datasets[0].data.length
|
||||
valuesCount: data.datasets[0].data.length,
|
||||
calculateRange: function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value, index) {
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
this.scale.setScaleSize();
|
||||
this.updateScaleRange(data.datasets);
|
||||
this.scale.calculateRange();
|
||||
this.scale.generateTicks();
|
||||
this.scale.buildYLabels();
|
||||
},
|
||||
updateScaleRange: function(datasets){
|
||||
var valuesArray = (function(){
|
||||
var totalDataArray = [];
|
||||
helpers.each(datasets,function(dataset){
|
||||
if (dataset.data){
|
||||
totalDataArray = totalDataArray.concat(dataset.data);
|
||||
}
|
||||
else {
|
||||
helpers.each(dataset.points, function(point){
|
||||
totalDataArray.push(point.value);
|
||||
});
|
||||
}
|
||||
});
|
||||
return totalDataArray;
|
||||
})();
|
||||
|
||||
|
||||
var scaleSizes = (this.options.scaleOverride) ?
|
||||
{
|
||||
steps: this.options.scaleSteps,
|
||||
stepValue: this.options.scaleStepWidth,
|
||||
min: this.options.scaleStartValue,
|
||||
max: this.options.scaleStartValue + (this.options.scaleSteps * this.options.scaleStepWidth)
|
||||
} :
|
||||
helpers.calculateScaleRange(
|
||||
valuesArray,
|
||||
helpers.min([this.chart.width, this.chart.height])/2,
|
||||
this.options.scaleFontSize,
|
||||
this.options.scaleBeginAtZero,
|
||||
this.options.scaleIntegersOnly
|
||||
);
|
||||
|
||||
helpers.extend(
|
||||
this.scale,
|
||||
scaleSizes
|
||||
);
|
||||
|
||||
},
|
||||
addData : function(valuesArray,label){
|
||||
//Map the values array for each of the datasets
|
||||
this.scale.valuesCount++;
|
||||
@ -311,8 +323,9 @@
|
||||
xCenter: this.chart.width/2,
|
||||
yCenter: this.chart.height/2
|
||||
});
|
||||
this.updateScaleRange(this.datasets);
|
||||
this.scale.setScaleSize();
|
||||
|
||||
this.scale.calculateRange();
|
||||
this.scale.generateTicks();
|
||||
this.scale.buildYLabels();
|
||||
},
|
||||
draw : function(ease){
|
||||
@ -326,7 +339,7 @@
|
||||
//Transition each point first so that the line and point drawing isn't out of sync
|
||||
helpers.each(dataset.points,function(point,index){
|
||||
if (point.hasValue()){
|
||||
point.transition(this.scale.getPointPosition(index, this.scale.calculateCenterOffset(point.value)), easeDecimal);
|
||||
point.transition(easeDecimal);
|
||||
}
|
||||
},this);
|
||||
|
||||
|
||||
1155
src/Chart.Scale.js
Normal file
1155
src/Chart.Scale.js
Normal file
File diff suppressed because it is too large
Load Diff
502
src/Chart.Scatter.js
Normal file
502
src/Chart.Scatter.js
Normal file
@ -0,0 +1,502 @@
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
var root = this,
|
||||
Chart = root.Chart,
|
||||
helpers = Chart.helpers;
|
||||
|
||||
var defaultConfig = {
|
||||
hoverMode: 'single',
|
||||
scales: {
|
||||
xAxes: [{
|
||||
scaleType: "linear", // scatter should not use a dataset axis
|
||||
display: true,
|
||||
position: "bottom",
|
||||
id: "x-axis-1", // need an ID so datasets can reference the scale
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true,
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
},
|
||||
}],
|
||||
yAxes: [{
|
||||
scaleType: "linear", // only linear but allow scale type registration. This allows extensions to exist solely for log scale for instance
|
||||
display: true,
|
||||
position: "left",
|
||||
id: "y-axis-1",
|
||||
|
||||
// grid line settings
|
||||
gridLines: {
|
||||
show: true,
|
||||
color: "rgba(0, 0, 0, 0.05)",
|
||||
lineWidth: 1,
|
||||
drawOnChartArea: true,
|
||||
drawTicks: true, // draw ticks extending towards the label
|
||||
zeroLineWidth: 1,
|
||||
zeroLineColor: "rgba(0,0,0,0.25)",
|
||||
},
|
||||
|
||||
// scale numbers
|
||||
beginAtZero: false,
|
||||
integersOnly: false,
|
||||
override: null,
|
||||
|
||||
// label settings
|
||||
labels: {
|
||||
show: true,
|
||||
template: "<%=value%>",
|
||||
fontSize: 12,
|
||||
fontStyle: "normal",
|
||||
fontColor: "#666",
|
||||
fontFamily: "Helvetica Neue",
|
||||
}
|
||||
}],
|
||||
},
|
||||
|
||||
//Number - Tension of the bezier curve between points
|
||||
tension: 0.4,
|
||||
|
||||
//Number - Radius of each point dot in pixels
|
||||
pointRadius: 4,
|
||||
|
||||
//Number - Pixel width of point dot border
|
||||
pointBorderWidth: 1,
|
||||
|
||||
//Number - amount extra to add to the radius to cater for hit detection outside the drawn point
|
||||
pointHoverRadius: 20,
|
||||
|
||||
//Number - Pixel width of dataset border
|
||||
borderWidth: 2,
|
||||
|
||||
//String - A legend template
|
||||
legendTemplate: "<ul class=\"<%=name.toLowerCase()%>-legend\"><% for (var i=0; i<datasets.length; i++){%><li><span style=\"background-color:<%=datasets[i].borderColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>",
|
||||
|
||||
tooltipTemplate: "(<%= dataX %>, <%= dataY %>)",
|
||||
multiTooltipTemplate: "<%if (datasetLabel){%><%=datasetLabel%>: <%}%>(<%= dataX %>, <%= dataY %>)",
|
||||
|
||||
};
|
||||
|
||||
|
||||
Chart.Type.extend({
|
||||
name: "Scatter",
|
||||
defaults: defaultConfig,
|
||||
initialize: function() {
|
||||
//Custom Point Defaults
|
||||
this.PointClass = Chart.Point.extend({
|
||||
_chart: this.chart,
|
||||
offsetGridLines: this.options.offsetGridLines,
|
||||
borderWidth: this.options.pointBorderWidth,
|
||||
radius: this.options.pointRadius,
|
||||
hoverRadius: this.options.pointHoverRadius,
|
||||
});
|
||||
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
// Build Scale
|
||||
this.buildScale();
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
//Create a new line and its points for each dataset and piece of data
|
||||
helpers.each(this.data.datasets, function(dataset, datasetIndex) {
|
||||
dataset.metaDataset = new Chart.Line();
|
||||
dataset.metaData = [];
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new this.PointClass());
|
||||
}, this);
|
||||
|
||||
// Make sure each dataset is bound to an x and a y axis
|
||||
if (!dataset.xAxisID) {
|
||||
dataset.xAxisID = this.options.scales.xAxes[0].id;
|
||||
}
|
||||
|
||||
if (!dataset.yAxisID) {
|
||||
dataset.yAxisID = this.options.scales.yAxes[0].id;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// Set defaults for lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
dataset = helpers.merge(this.options, dataset);
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
_points: dataset.metaData,
|
||||
_datasetIndex: datasetIndex,
|
||||
_chart: this.chart,
|
||||
});
|
||||
// Copy to view model
|
||||
dataset.metaDataset.save();
|
||||
}, this);
|
||||
|
||||
// Set defaults for points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
|
||||
helpers.extend(point, {
|
||||
x: xScale.getPixelForValue(index),
|
||||
y: this.chartArea.bottom,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart
|
||||
});
|
||||
|
||||
// Default bezier control points
|
||||
helpers.extend(point, {
|
||||
controlPointPreviousX: this.previousPoint(dataset, index).x,
|
||||
controlPointPreviousY: this.nextPoint(dataset, index).y,
|
||||
controlPointNextX: this.previousPoint(dataset, index).x,
|
||||
controlPointNextY: this.nextPoint(dataset, index).y,
|
||||
});
|
||||
// Copy to view model
|
||||
point.save();
|
||||
}, this);
|
||||
|
||||
// Create tooltip instance exclusively for this chart with some defaults.
|
||||
this.tooltip = new Chart.Tooltip({
|
||||
_chart: this.chart,
|
||||
_data: this.data,
|
||||
_options: this.options,
|
||||
}, this);
|
||||
|
||||
this.update();
|
||||
},
|
||||
nextPoint: function(collection, index) {
|
||||
return collection[index - 1] || collection[index];
|
||||
},
|
||||
previousPoint: function(collection, index) {
|
||||
return collection[index + 1] || collection[index];
|
||||
},
|
||||
events: function(e) {
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
return this;
|
||||
}
|
||||
|
||||
this.lastActive = this.lastActive || [];
|
||||
|
||||
// Find Active Elements
|
||||
this.active = function() {
|
||||
switch (this.options.hoverMode) {
|
||||
case 'single':
|
||||
return this.getElementAtEvent(e);
|
||||
case 'label':
|
||||
return this.getElementsAtEvent(e);
|
||||
case 'dataset':
|
||||
return this.getDatasetAtEvent(e);
|
||||
default:
|
||||
return e;
|
||||
}
|
||||
}.call(this);
|
||||
|
||||
// On Hover hook
|
||||
if (this.options.onHover) {
|
||||
this.options.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
// Remove styling for last active (even if it may still be active)
|
||||
if (this.lastActive.length) {
|
||||
switch (this.options.hoverMode) {
|
||||
case 'single':
|
||||
this.lastActive[0].backgroundColor = this.data.datasets[this.lastActive[0]._datasetIndex].pointBackgroundColor;
|
||||
this.lastActive[0].borderColor = this.data.datasets[this.lastActive[0]._datasetIndex].pointBorderColor;
|
||||
this.lastActive[0].borderWidth = this.data.datasets[this.lastActive[0]._datasetIndex].pointBorderWidth;
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.lastActive.length; i++) {
|
||||
this.lastActive[i].backgroundColor = this.data.datasets[this.lastActive[i]._datasetIndex].pointBackgroundColor;
|
||||
this.lastActive[i].borderColor = this.data.datasets[this.lastActive[i]._datasetIndex].pointBorderColor;
|
||||
this.lastActive[i].borderWidth = this.data.datasets[this.lastActive[0]._datasetIndex].pointBorderWidth;
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in hover styling
|
||||
if (this.active.length && this.options.hoverMode) {
|
||||
switch (this.options.hoverMode) {
|
||||
case 'single':
|
||||
this.active[0].backgroundColor = this.data.datasets[this.active[0]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[0].backgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[0].borderColor = this.data.datasets[this.active[0]._datasetIndex].hoverBorderColor || helpers.color(this.active[0].borderColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[0].borderWidth = this.data.datasets[this.active[0]._datasetIndex].borderWidth + 10;
|
||||
break;
|
||||
case 'label':
|
||||
for (var i = 0; i < this.active.length; i++) {
|
||||
this.active[i].backgroundColor = this.data.datasets[this.active[i]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[i].backgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[i].borderColor = this.data.datasets[this.active[i]._datasetIndex].hoverBorderColor || helpers.color(this.active[i].borderColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[i].borderWidth = this.data.datasets[this.active[i]._datasetIndex].borderWidth + 2;
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
break;
|
||||
default:
|
||||
// Don't change anything
|
||||
}
|
||||
}
|
||||
|
||||
// Built in Tooltips
|
||||
if (this.options.showTooltips) {
|
||||
|
||||
// The usual updates
|
||||
this.tooltip.initialize();
|
||||
|
||||
// Active
|
||||
if (this.active.length) {
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 1,
|
||||
_active: this.active,
|
||||
});
|
||||
|
||||
this.tooltip.update();
|
||||
} else {
|
||||
// Inactive
|
||||
helpers.extend(this.tooltip, {
|
||||
opacity: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Hover animations
|
||||
this.tooltip.pivot();
|
||||
|
||||
if (!this.animating) {
|
||||
var changed;
|
||||
|
||||
helpers.each(this.active, function(element, index) {
|
||||
if (element !== this.lastActive[index]) {
|
||||
changed = true;
|
||||
}
|
||||
}, this);
|
||||
|
||||
// If entering, leaving, or changing elements, animate the change via pivot
|
||||
if ((!this.lastActive.length && this.active.length) ||
|
||||
(this.lastActive.length && !this.active.length) ||
|
||||
(this.lastActive.length && this.active.length && changed)) {
|
||||
|
||||
this.stop();
|
||||
this.render(this.options.hoverAnimationDuration);
|
||||
}
|
||||
}
|
||||
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
|
||||
},
|
||||
update: function() {
|
||||
Chart.scaleService.fitScalesForChart(this, this.chart.width, this.chart.height);
|
||||
|
||||
// Update the lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
helpers.extend(dataset.metaDataset, {
|
||||
backgroundColor: dataset.backgroundColor || this.options.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.borderColor,
|
||||
tension: dataset.tension || this.options.tension,
|
||||
scaleTop: this.chartArea.top,
|
||||
scaleBottom: this.chartArea.bottom,
|
||||
_points: dataset.metaData,
|
||||
_datasetIndex: datasetIndex,
|
||||
});
|
||||
dataset.metaDataset.pivot();
|
||||
});
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var xScale = this.scales[this.data.datasets[datasetIndex].xAxisID];
|
||||
var yScale = this.scales[this.data.datasets[datasetIndex].yAxisID];
|
||||
|
||||
helpers.extend(point, {
|
||||
x: xScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].x),
|
||||
y: yScale.getPixelForValue(this.data.datasets[datasetIndex].data[index].y),
|
||||
dataX: this.data.datasets[datasetIndex].data[index].x,
|
||||
dataY: this.data.datasets[datasetIndex].data[index].y,
|
||||
label: '', // so that the multitooltip looks ok
|
||||
value: this.data.datasets[datasetIndex].data[index].y, // for legacy reasons
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
// Appearance
|
||||
hoverBackgroundColor: this.data.datasets[datasetIndex].pointHoverBackgroundColor || this.options.pointHoverBackgroundColor,
|
||||
hoverBorderColor: this.data.datasets[datasetIndex].pointHoverBorderColor || this.options.pointHoverBorderColor,
|
||||
hoverRadius: this.data.datasets[datasetIndex].pointHoverRadius || this.options.pointHoverRadius,
|
||||
radius: this.data.datasets[datasetIndex].pointRadius || this.options.pointRadius,
|
||||
borderWidth: this.data.datasets[datasetIndex].pointBorderWidth || this.options.pointBorderWidth,
|
||||
borderColor: this.data.datasets[datasetIndex].pointBorderColor || this.options.pointBorderColor,
|
||||
backgroundColor: this.data.datasets[datasetIndex].pointBackgroundColor || this.options.pointBackgroundColor,
|
||||
tension: this.data.datasets[datasetIndex].metaDataset.tension,
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
});
|
||||
}, this);
|
||||
|
||||
// Update control points for the bezier curve
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
var controlPoints = helpers.splineCurve(
|
||||
this.previousPoint(dataset, index),
|
||||
point,
|
||||
this.nextPoint(dataset, index),
|
||||
point.tension
|
||||
);
|
||||
|
||||
point.controlPointPreviousX = controlPoints.previous.x;
|
||||
point.controlPointNextX = controlPoints.next.x;
|
||||
|
||||
// Prevent the bezier going outside of the bounds of the graph
|
||||
|
||||
// Cap puter bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.next.y > this.chartArea.bottom) {
|
||||
point.controlPointNextY = this.chartArea.bottom;
|
||||
} else if (controlPoints.next.y < this.chartArea.top) {
|
||||
point.controlPointNextY = this.chartArea.top;
|
||||
} else {
|
||||
point.controlPointNextY = controlPoints.next.y;
|
||||
}
|
||||
|
||||
// Cap inner bezier handles to the upper/lower scale bounds
|
||||
if (controlPoints.previous.y > this.chartArea.bottom) {
|
||||
point.controlPointPreviousY = this.chartArea.bottom;
|
||||
} else if (controlPoints.previous.y < this.chartArea.top) {
|
||||
point.controlPointPreviousY = this.chartArea.top;
|
||||
} else {
|
||||
point.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function() {
|
||||
var self = this;
|
||||
|
||||
var calculateXRange = function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
// Only set the scale range for datasets that actually use this axis
|
||||
if (dataset.xAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value) {
|
||||
if (this.min === null) {
|
||||
this.min = value.x;
|
||||
} else if (value.x < this.min) {
|
||||
this.min = value.x;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value.x;
|
||||
} else if (value.x > this.max) {
|
||||
this.max = value.x;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
var calculateYRange = function() {
|
||||
this.min = null;
|
||||
this.max = null;
|
||||
|
||||
helpers.each(self.data.datasets, function(dataset) {
|
||||
if (dataset.yAxisID === this.id) {
|
||||
helpers.each(dataset.data, function(value) {
|
||||
if (this.min === null) {
|
||||
this.min = value.y;
|
||||
} else if (value.y < this.min) {
|
||||
this.min = value.y;
|
||||
}
|
||||
|
||||
if (this.max === null) {
|
||||
this.max = value.y;
|
||||
} else if (value.y > this.max) {
|
||||
this.max = value.y;
|
||||
}
|
||||
}, this);
|
||||
}
|
||||
}, this);
|
||||
};
|
||||
|
||||
// Map of scale ID to scale object so we can lookup later
|
||||
this.scales = {};
|
||||
|
||||
helpers.each(this.options.scales.xAxes, function(xAxisOptions) {
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(xAxisOptions.scaleType);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: xAxisOptions,
|
||||
calculateRange: calculateXRange,
|
||||
id: xAxisOptions.id,
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
|
||||
helpers.each(this.options.scales.yAxes, function(yAxisOptions) {
|
||||
var ScaleClass = Chart.scales.getScaleConstructor(yAxisOptions.scaleType);
|
||||
var scale = new ScaleClass({
|
||||
ctx: this.chart.ctx,
|
||||
options: yAxisOptions,
|
||||
calculateRange: calculateYRange,
|
||||
id: yAxisOptions.id,
|
||||
});
|
||||
|
||||
this.scales[scale.id] = scale;
|
||||
}, this);
|
||||
},
|
||||
redraw: function() {
|
||||
|
||||
},
|
||||
draw: function(ease) {
|
||||
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
// Draw all the scales
|
||||
helpers.each(this.scales, function(scale) {
|
||||
scale.draw(this.chartArea);
|
||||
}, this);
|
||||
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
// Transition Point Locations
|
||||
helpers.each(dataset.metaData, function(point, index) {
|
||||
point.transition(easingDecimal);
|
||||
}, this);
|
||||
|
||||
// Transition and Draw the line
|
||||
dataset.metaDataset.transition(easingDecimal).draw();
|
||||
|
||||
// Draw the points
|
||||
helpers.each(dataset.metaData, function(point) {
|
||||
point.draw();
|
||||
});
|
||||
}, this);
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
}).call(this);
|
||||
Loading…
x
Reference in New Issue
Block a user