mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-14 04:06:49 +01:00
Playground and big changes
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,3 +5,5 @@ node_modules/*
|
||||
custom/*
|
||||
|
||||
docs/index.md
|
||||
|
||||
bower_components/
|
||||
|
||||
6
Chart.min.js
vendored
6
Chart.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -7,5 +7,9 @@
|
||||
"main": [
|
||||
"Chart.js"
|
||||
],
|
||||
"dependencies": {}
|
||||
}
|
||||
"devDependencies": {
|
||||
"angular": "~1.3.15",
|
||||
"jquery": "~2.1.4",
|
||||
"flatstrap": "~3.3.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"gulp-uglify": "~0.2.x",
|
||||
"gulp-util": "~2.2.x",
|
||||
"inquirer": "^0.5.1",
|
||||
"jquery": "^2.1.4",
|
||||
"onecolor": "^2.5.0",
|
||||
"semver": "^3.0.1"
|
||||
},
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
<head>
|
||||
<title>Bar Chart</title>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -14,10 +14,10 @@
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100)
|
||||
return (Math.random() > 0.5 ? 1.0 : -1.0) * Math.round(Math.random() * 100);
|
||||
};
|
||||
var randomColorFactor = function() {
|
||||
return Math.round(Math.random() * 255)
|
||||
return Math.round(Math.random() * 255);
|
||||
};
|
||||
|
||||
var barChartData = {
|
||||
@@ -30,26 +30,38 @@
|
||||
label: 'Dataset 2',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 4',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: 'Dataset 3',
|
||||
backgroundColor: "rgba(151,187,205,0.5)",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
|
||||
}
|
||||
};
|
||||
window.onload = function() {
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myBar = new Chart(ctx).Bar(barChartData, {
|
||||
responsive: true,
|
||||
hoverMode: 'label',
|
||||
scaleBeginAtZero: false,
|
||||
hoverAnimationDuration: 400
|
||||
hoverAnimationDuration: 400,
|
||||
stacked: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$('#randomizeData').click(function() {
|
||||
barChartData.datasets[0].backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
barChartData.datasets[0].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
|
||||
barChartData.datasets[1].backgroundColor = 'rgba(' + randomColorFactor() + ',' + randomColorFactor() + ',' + randomColorFactor() + ',.7)';
|
||||
barChartData.datasets[1].data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
$.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>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<head>
|
||||
<title>Line Chart</title>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
|
||||
<script src="../node_modules/jquery/dist/jquery.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -16,7 +16,7 @@
|
||||
<button id="randomizeData">Randomize Data</button>
|
||||
<script>
|
||||
var randomScalingFactor = function() {
|
||||
return Math.round(Math.random() * 100);
|
||||
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') + ')';
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
$.each(lineChartData.datasets, function(i, dataset) {
|
||||
dataset.borderColor = randomColor(0.4);
|
||||
dataset.backgroundColor = randomColor(0.1);
|
||||
dataset.backgroundColor = randomColor(1);
|
||||
dataset.pointBorderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(0.5);
|
||||
dataset.pointBorderWidth = 1;
|
||||
@@ -47,7 +47,8 @@
|
||||
var ctx = document.getElementById("canvas").getContext("2d");
|
||||
window.myLine = new Chart(ctx).Line(lineChartData, {
|
||||
responsive: true,
|
||||
hoverMode: 'label'
|
||||
hoverMode: 'label',
|
||||
stacked: true
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
72
samples/playground.html
Normal file
72
samples/playground.html
Normal file
@@ -0,0 +1,72 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<title>Line Chart</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="../bower_components/flatstrap/dist/css/flatstrap.min.css" rel="stylesheet" type="text/css" />
|
||||
<style>
|
||||
input[type="color"] {
|
||||
border: none;
|
||||
color: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 60px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.options,
|
||||
.datasets {
|
||||
width: 50%;
|
||||
float: left;
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body ng-app="app" ng-controller="mainController" class="container">
|
||||
<div style="width:100%">
|
||||
<div>
|
||||
<canvas id="canvas" height="350" width="600"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
<label>Chart Type: </label>
|
||||
<select ng-model="chartType" ng-change="init()">
|
||||
<option ng-repeat="type in chartTypes" ng-selected="type.selected" value="{{type.value}}">{{type.name}}</option>
|
||||
</select>
|
||||
<hr>
|
||||
<div class="options">
|
||||
<textarea ng-model="options" style="width:100%;height:400px;"></textarea>
|
||||
</div>
|
||||
<div class="datasets">
|
||||
<button ng-click="randomizeData()" class="btn btn-primary">Randomize Data</button>
|
||||
<hr>
|
||||
<div ng-repeat="dataset in chart.data.datasets">
|
||||
<pre>{{dataset.data}}</pre>
|
||||
<div>
|
||||
borderWidth:
|
||||
<input type="number" ng-model="dataset.borderWidth" ng-change="_chart.update()" />
|
||||
</div>
|
||||
<div>
|
||||
hoverBorderWidth:
|
||||
<input type="number" ng-model="dataset.hoverBorderWidth" ng-change="_chart.update()" />
|
||||
</div>
|
||||
<div>
|
||||
pointBorderWidth:
|
||||
<input type="number" ng-model="dataset.pointBorderWidth" ng-change="_chart.update()" />
|
||||
</div>
|
||||
<div>
|
||||
pointHoverBorderWidth:
|
||||
<input type="number" ng-model="dataset.pointHoverBorderWidth" ng-change="_chart.update()" />
|
||||
</div>
|
||||
<hr>
|
||||
</div>
|
||||
</div>
|
||||
<script src="../Chart.js"></script>
|
||||
<script src="../bower_components/jquery/dist/jquery.min.js"></script>
|
||||
<script src="../bower_components/angular/angular.min.js"></script>
|
||||
<script src="playground.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
156
samples/playground.js
Normal file
156
samples/playground.js
Normal file
@@ -0,0 +1,156 @@
|
||||
(function() {
|
||||
|
||||
var module = angular.module('app', []);
|
||||
|
||||
module.controller('mainController', function($scope, $timeout) {
|
||||
angular.extend($scope, {
|
||||
|
||||
chart: {},
|
||||
chartTypes: chartTypes(),
|
||||
|
||||
init: init,
|
||||
randomizeData: randomizeData,
|
||||
});
|
||||
|
||||
|
||||
init();
|
||||
|
||||
|
||||
|
||||
function init() {
|
||||
if ($scope._chart) {
|
||||
$scope._chart.destroy();
|
||||
}
|
||||
$scope.ctx = $('canvas')[0].getContext("2d");
|
||||
buildData();
|
||||
buildChart();
|
||||
|
||||
$scope.$watch('options', function(val) {
|
||||
try {
|
||||
angular.extend($scope.chart.options, JSON.parse(val));
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}, true);
|
||||
|
||||
$scope.$watch('chart.options', function() {
|
||||
if ($scope._chart) {
|
||||
$scope._chart.update();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
function chartTypes() {
|
||||
return [{
|
||||
name: 'Line/Scatter',
|
||||
value: 'Line/Scatter',
|
||||
selected: true
|
||||
}, {
|
||||
name: 'Bar',
|
||||
value: 'Bar',
|
||||
}, {
|
||||
name: 'Pie/Doughnut',
|
||||
value: 'Pie/Doughnut',
|
||||
}, {
|
||||
name: 'Polar',
|
||||
value: 'Polar',
|
||||
}, {
|
||||
name: 'Radar',
|
||||
value: 'Radar',
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
|
||||
function buildData() {
|
||||
$scope.chart.data = groupData();
|
||||
$scope.chart.options = {
|
||||
stacked: true,
|
||||
hoverMode: 'label'
|
||||
};
|
||||
$scope.options = stringify($scope.chart.options);
|
||||
}
|
||||
|
||||
function buildChart() {
|
||||
$scope._chart = new Chart($scope.ctx)[$scope.chartType || 'Line']($scope.chart);
|
||||
}
|
||||
|
||||
function stringify(o) {
|
||||
var cache = [];
|
||||
var result = JSON.stringify(o, function(key, value) {
|
||||
if (typeof value === 'object' && value !== null) {
|
||||
if (cache.indexOf(value) !== -1) {
|
||||
// Circular reference found, discard key
|
||||
return;
|
||||
}
|
||||
// Store value in our collection
|
||||
cache.push(value);
|
||||
}
|
||||
return value;
|
||||
}, 1);
|
||||
cache = null; // Enable garbage collection
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function groupData() {
|
||||
return {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "Dataset 1",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
backgroundColor: randomColor(0.8),
|
||||
}, {
|
||||
label: "Dataset 2",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
backgroundColor: randomColor(0.8),
|
||||
}, {
|
||||
label: "Dataset 3",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()],
|
||||
backgroundColor: randomColor(0.8),
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
function flatData() {
|
||||
return {
|
||||
labels: ["January", "February", "March", "April", "May", "June", "July"],
|
||||
datasets: [{
|
||||
label: "My First dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}, {
|
||||
label: "My Second dataset",
|
||||
data: [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()]
|
||||
}]
|
||||
};
|
||||
}
|
||||
|
||||
function randomizeData() {
|
||||
|
||||
angular.forEach($scope.chart.data.datasets, function(dataset) {
|
||||
|
||||
dataset.data = [randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor(), randomScalingFactor()];
|
||||
dataset.backgroundColor = randomColor(.8);
|
||||
dataset.borderColor = randomColor(0.7);
|
||||
dataset.pointBackgroundColor = randomColor(1);
|
||||
dataset.pointBorderColor = randomColor(1);
|
||||
|
||||
|
||||
$scope._chart.update();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function randomScalingFactor() {
|
||||
return Math.round(Math.random() * 100 * (Math.random() > 0.5 ? -1 : 1));
|
||||
}
|
||||
|
||||
function randomColor(opacity) {
|
||||
return 'rgba(' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + Math.round(Math.random() * 255) + ',' + (typeof opacity != "undefined" ? opacity : '.3') + ')';
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
162
src/Chart.Bar.js
162
src/Chart.Bar.js
@@ -34,6 +34,9 @@
|
||||
//Number - Spacing between data sets within X values
|
||||
barDatasetSpacing: 1,
|
||||
|
||||
//Boolean - Whether bars should be rendered on a percentage base
|
||||
relativeBars: false,
|
||||
|
||||
//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].backgroundColor%>\"></span><%if(datasets[i].label){%><%=datasets[i].label%><%}%></li><%}%></ul>"
|
||||
|
||||
@@ -50,26 +53,109 @@
|
||||
|
||||
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) {
|
||||
//Reusable method for calculating the xPosition of a given bar based on datasetIndex & width of the bar
|
||||
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);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Events
|
||||
@@ -92,11 +178,12 @@
|
||||
}, this);
|
||||
|
||||
// Set defaults for bars
|
||||
this.eachElement(function(bar, index, datasetIndex) {
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
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.calculateBaseY(),
|
||||
y: this.scale.calculateBarY(this.data.datasets, datasetIndex, index, this.data.datasets[datasetIndex].data[index]),
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
});
|
||||
@@ -167,13 +254,13 @@
|
||||
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].backgroundColor = this.data.datasets[this.active[0]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[0].backgroundColor).saturate(0.8).darken(0.2).rgbString();
|
||||
this.active[0].borderColor = this.data.datasets[this.active[0]._datasetIndex].hoverBorderColor || helpers.color(this.active[0].borderColor).saturate(0.8).darken(0.2).rgbString();
|
||||
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].backgroundColor = this.data.datasets[this.active[i]._datasetIndex].hoverBackgroundColor || helpers.color(this.active[i].backgroundColor).saturate(0.8).darken(0.2).rgbString();
|
||||
this.active[i].borderColor = this.data.datasets[this.active[i]._datasetIndex].hoverBorderColor || helpers.color(this.active[i].borderColor).saturate(0.8).darken(0.2).rgbString();
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
@@ -233,30 +320,23 @@
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
},
|
||||
// Calculate the base point for the bar.
|
||||
calculateBaseY: function() {
|
||||
var base = this.scale.endPoint;
|
||||
|
||||
if (this.scale.beginAtZero || ((this.scale.min <= 0 && this.scale.max >= 0) || (this.scale.min >= 0 && this.scale.max <= 0))) {
|
||||
base = this.scale.calculateY(0);
|
||||
base += this.options.scaleGridLineWidth;
|
||||
} else if (this.scale.min < 0 && this.scale.max < 0) {
|
||||
// All values are negative. Use the top as the base
|
||||
base = this.scale.startPoint;
|
||||
}
|
||||
|
||||
return base;
|
||||
},
|
||||
update: function() {
|
||||
|
||||
this.scale.update();
|
||||
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
helpers.extend(bar, {
|
||||
width: this.scale.calculateBarWidth(this.data.datasets.length),
|
||||
x: this.scale.calculateBarX(this.data.datasets.length, datasetIndex, index),
|
||||
y: this.scale.calculateY(this.data.datasets[datasetIndex].data[index]),
|
||||
value: this.data.datasets[datasetIndex].data[index],
|
||||
});
|
||||
bar.pivot();
|
||||
}, this);
|
||||
|
||||
this.eachElement(function(bar, index, dataset, datasetIndex) {
|
||||
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),
|
||||
label: this.data.labels[index],
|
||||
datasetLabel: this.data.datasets[datasetIndex].label,
|
||||
borderColor: this.data.datasets[datasetIndex].borderColor,
|
||||
@@ -275,10 +355,31 @@
|
||||
|
||||
var dataTotal = function() {
|
||||
var values = [];
|
||||
self.eachValue(function(value) {
|
||||
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;
|
||||
} else {
|
||||
values[index] += value;
|
||||
}
|
||||
}
|
||||
});
|
||||
return values.concat(negativeValues);
|
||||
}
|
||||
|
||||
self.eachValue(function(value, index) {
|
||||
values.push(value);
|
||||
});
|
||||
|
||||
return values;
|
||||
|
||||
};
|
||||
|
||||
var scaleOptions = {
|
||||
@@ -330,7 +431,7 @@
|
||||
},
|
||||
// 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.calculateBaseY();
|
||||
var base = this.scale.zeroPoint;
|
||||
this.eachElement(function(element, index, datasetIndex) {
|
||||
helpers.extend(element, {
|
||||
y: base,
|
||||
@@ -348,12 +449,7 @@
|
||||
|
||||
//Draw all the bars for each dataset
|
||||
this.eachElement(function(bar, index, datasetIndex) {
|
||||
if (bar.hasValue()) {
|
||||
// Update the bar basepoint
|
||||
bar.base = this.calculateBaseY();
|
||||
//Transition
|
||||
bar.transition(easingDecimal).draw();
|
||||
}
|
||||
bar.transition(easingDecimal).draw();
|
||||
}, this);
|
||||
|
||||
// Finally draw the tooltip
|
||||
|
||||
@@ -113,9 +113,12 @@
|
||||
//String / Boolean - Hover mode for events.
|
||||
hoverMode: 'single', // 'label', 'dataset', 'false'
|
||||
|
||||
//Function - Custom hover handler
|
||||
//Function(event) - Custom hover handler
|
||||
onHover: null,
|
||||
|
||||
//Function(event, clickedElements) - Custom click handler
|
||||
onClick: null,
|
||||
|
||||
//Function - Custom hover handler
|
||||
hoverAnimationDuration: 400,
|
||||
|
||||
@@ -125,8 +128,8 @@
|
||||
// Boolean - Determines whether to draw built-in tooltip or call custom tooltip function
|
||||
customTooltips: false,
|
||||
|
||||
// Array - Array of string names to attach tooltip events
|
||||
tooltipEvents: ["mousemove", "touchstart", "touchmove", "mouseout"],
|
||||
// Array - Array of string names to attach interaction events
|
||||
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
|
||||
|
||||
// String - Tooltip background colour
|
||||
tooltipBackgroundColor: "rgba(0,0,0,0.8)",
|
||||
@@ -186,7 +189,7 @@
|
||||
onAnimationComplete: function() {},
|
||||
|
||||
// Color String - Used for undefined Colros
|
||||
colorFallback: 'rgba(0,0,0,0.1)',
|
||||
defaultColor: 'rgba(0,0,0,0.1)',
|
||||
|
||||
}
|
||||
};
|
||||
@@ -910,19 +913,20 @@
|
||||
//Destroy method on the chart will remove the instance of the chart from this reference.
|
||||
Chart.instances = {};
|
||||
|
||||
Chart.Type = function(data, options, chart) {
|
||||
this.options = options;
|
||||
this.chart = chart;
|
||||
Chart.Type = function(config, instance) {
|
||||
this.data = config.data;
|
||||
this.options = config.options;
|
||||
this.chart = instance;
|
||||
this.id = uid();
|
||||
//Add the chart instance to the global namespace
|
||||
Chart.instances[this.id] = this;
|
||||
|
||||
// Initialize is always called when a chart type is created
|
||||
// By default it is a no op, but it should be extended
|
||||
if (options.responsive) {
|
||||
if (this.options.responsive) {
|
||||
this.resize();
|
||||
}
|
||||
this.initialize.call(this, data);
|
||||
this.initialize.call(this);
|
||||
};
|
||||
|
||||
//Core methods that'll be a part of every chart type
|
||||
@@ -1087,9 +1091,9 @@
|
||||
Chart.types[chartName] = ChartType;
|
||||
|
||||
//Register this new chart type in the Chart prototype
|
||||
Chart.prototype[chartName] = function(data, options) {
|
||||
var config = merge(Chart.defaults.global, Chart.defaults[chartName], options || {});
|
||||
return new ChartType(data, config, this);
|
||||
Chart.prototype[chartName] = function(config) {
|
||||
helpers.extend(config.options, merge(Chart.defaults.global, Chart.defaults[chartName], config.options || {}));
|
||||
return new ChartType(config, this);
|
||||
};
|
||||
} else {
|
||||
warn("Name not provided for this chart, so it hasn't been registered");
|
||||
@@ -1196,7 +1200,8 @@
|
||||
var vm = this._vm;
|
||||
return {
|
||||
x: vm.x,
|
||||
y: vm.y
|
||||
y: vm.y,
|
||||
padding: vm.radius + vm.borderWidth
|
||||
};
|
||||
},
|
||||
draw: function() {
|
||||
@@ -1211,10 +1216,10 @@
|
||||
ctx.arc(vm.x, vm.y, vm.radius, 0, Math.PI * 2);
|
||||
ctx.closePath();
|
||||
|
||||
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.colorFallback;
|
||||
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.colorFallback;
|
||||
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
|
||||
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.defaultColor;
|
||||
|
||||
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.colorFallback;
|
||||
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
|
||||
|
||||
ctx.fill();
|
||||
ctx.stroke();
|
||||
@@ -1229,9 +1234,41 @@
|
||||
var vm = this._vm;
|
||||
var ctx = this._chart.ctx;
|
||||
|
||||
//Draw the line between all the points
|
||||
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.colorFallback;
|
||||
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.colorFallback;
|
||||
// Draw the background first (so the border is always on top)
|
||||
helpers.each(vm._points, function(point, index) {
|
||||
if (index === 0) {
|
||||
ctx.moveTo(point._vm.x, point._vm.y);
|
||||
} else {
|
||||
if (vm._tension > 0 || 1) {
|
||||
var previous = this.previousPoint(point, vm._points, index);
|
||||
|
||||
ctx.bezierCurveTo(
|
||||
previous._vm.controlPointNextX,
|
||||
previous._vm.controlPointNextY,
|
||||
point._vm.controlPointPreviousX,
|
||||
point._vm.controlPointPreviousY,
|
||||
point._vm.x,
|
||||
point._vm.y
|
||||
);
|
||||
} else {
|
||||
ctx.lineTo(point._vm.x, point._vm.y);
|
||||
}
|
||||
}
|
||||
}, this);
|
||||
|
||||
if (vm._points.length > 0) {
|
||||
//Round off the line by going to the base of the chart, back to the start, then fill.
|
||||
ctx.lineTo(vm._points[vm._points.length - 1].x, vm.scaleZero);
|
||||
ctx.lineTo(vm._points[0].x, vm.scaleZero);
|
||||
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.defaultColor;
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
|
||||
|
||||
// Now draw the line between all the points with any borders
|
||||
ctx.lineWidth = vm.borderWidth || Chart.defaults.global.defaultColor;
|
||||
ctx.strokeStyle = vm.borderColor || Chart.defaults.global.defaultColor;
|
||||
ctx.beginPath();
|
||||
|
||||
helpers.each(vm._points, function(point, index) {
|
||||
@@ -1255,16 +1292,9 @@
|
||||
}
|
||||
}, this);
|
||||
|
||||
|
||||
ctx.stroke();
|
||||
|
||||
if (vm._points.length > 0) {
|
||||
//Round off the line by going to the base of the chart, back to the start, then fill.
|
||||
ctx.lineTo(vm._points[vm._points.length - 1].x, vm.scaleBottom);
|
||||
ctx.lineTo(vm._points[0].x, vm.scaleBottom);
|
||||
ctx.fillStyle = vm.backgroundColor || Chart.defaults.global.colorFallback;
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
}
|
||||
},
|
||||
previousPoint: function(point, collection, index) {
|
||||
return helpers.findPreviousWhere(collection, function() {
|
||||
@@ -1442,6 +1472,7 @@
|
||||
helpers.extend(this, {
|
||||
x: Math.round(tooltipPosition.x),
|
||||
y: Math.round(tooltipPosition.y),
|
||||
caretPadding: tooltipPosition.padding
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -1562,7 +1593,7 @@
|
||||
vm.yAlign = "above";
|
||||
|
||||
//Distance between the actual element.y position and the start of the tooltip caret
|
||||
var caretPadding = vm.caretPadding = 2;
|
||||
var caretPadding = vm.caretPadding || 2;
|
||||
|
||||
var tooltipWidth = ctx.measureText(vm.text).width + 2 * vm.xPadding,
|
||||
tooltipRectHeight = vm.fontSize + 2 * vm.yPadding,
|
||||
@@ -1723,14 +1754,14 @@
|
||||
|
||||
// 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 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
|
||||
|
||||
@@ -9,39 +9,50 @@
|
||||
|
||||
///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,
|
||||
|
||||
|
||||
|
||||
//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: 4,
|
||||
|
||||
//Number - Radius of each point dot in pixels
|
||||
pointRadius: 3,
|
||||
//Number - Pixel width of point dot border
|
||||
pointBorderWidth: 1,
|
||||
//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
|
||||
pointHoverRadius: 20,
|
||||
pointHitRadius: 6,
|
||||
|
||||
//Number - Pixel width of dataset border
|
||||
borderWidth: 2,
|
||||
//Number - Pixel width of dataset border on hover
|
||||
hoverBorderWidth: 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>",
|
||||
|
||||
//Boolean - Whether to horizontally center the label and point dot inside the grid
|
||||
offsetGridLines: false
|
||||
|
||||
|
||||
};
|
||||
|
||||
@@ -49,23 +60,39 @@
|
||||
Chart.Type.extend({
|
||||
name: "Line",
|
||||
defaults: defaultConfig,
|
||||
initialize: function(data) {
|
||||
// Save data as a source for updating of values & methods
|
||||
this.data = data;
|
||||
|
||||
//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,
|
||||
});
|
||||
initialize: function() {
|
||||
|
||||
// Events
|
||||
helpers.bindEvents(this, this.options.tooltipEvents, this.onHover);
|
||||
helpers.bindEvents(this, this.options.events, this.events);
|
||||
|
||||
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);
|
||||
|
||||
|
||||
@@ -74,19 +101,20 @@
|
||||
dataset.metaDataset = new Chart.Line();
|
||||
dataset.metaData = [];
|
||||
helpers.each(dataset.data, function(dataPoint, index) {
|
||||
dataset.metaData.push(new this.PointClass());
|
||||
dataset.metaData.push(new Chart.Point());
|
||||
}, this);
|
||||
}, 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
|
||||
// Fill in dataset defaults from options
|
||||
helpers.extend(dataset, helpers.merge(this.options, dataset));
|
||||
// Copy to view modele
|
||||
dataset.metaDataset.save();
|
||||
}, this);
|
||||
|
||||
@@ -94,7 +122,7 @@
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
helpers.extend(point, {
|
||||
x: this.scale.calculateX(index),
|
||||
y: this.scale.endPoint,
|
||||
y: this.scale.calculateY(0),
|
||||
_datasetIndex: datasetIndex,
|
||||
_index: index,
|
||||
_chart: this.chart
|
||||
@@ -126,8 +154,206 @@
|
||||
previousPoint: function(collection, index) {
|
||||
return collection[index + 1] || collection[index];
|
||||
},
|
||||
onHover: function(e) {
|
||||
update: function() {
|
||||
|
||||
// Update the scale
|
||||
this.scale.update();
|
||||
|
||||
// Update the lines
|
||||
this.eachDataset(function(dataset, datasetIndex) {
|
||||
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),
|
||||
// Appearance
|
||||
tension: dataset.tension || this.options.tension,
|
||||
backgroundColor: dataset.backgroundColor || this.options.backgroundColor,
|
||||
borderWidth: dataset.borderWidth || this.options.borderWidth,
|
||||
borderColor: dataset.borderColor || this.options.borderColor,
|
||||
});
|
||||
dataset.metaDataset.pivot();
|
||||
});
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
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),
|
||||
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,
|
||||
});
|
||||
}, 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.scale.endPoint) {
|
||||
point.controlPointNextY = this.scale.endPoint;
|
||||
} else if (controlPoints.next.y < this.scale.startPoint) {
|
||||
point.controlPointNextY = this.scale.startPoint;
|
||||
} 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;
|
||||
} else {
|
||||
point.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function(labels) {
|
||||
var self = this;
|
||||
|
||||
var dataTotal = function() {
|
||||
var values = [];
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
return values.concat(negativeValues);
|
||||
}
|
||||
|
||||
self.eachValue(function(value, index) {
|
||||
values.push(value);
|
||||
});
|
||||
|
||||
return values;
|
||||
|
||||
};
|
||||
|
||||
var scaleOptions = {
|
||||
templateString: this.options.scaleLabel,
|
||||
height: this.chart.height,
|
||||
width: this.chart.width,
|
||||
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);
|
||||
},
|
||||
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
|
||||
};
|
||||
|
||||
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);
|
||||
},
|
||||
redraw: function() {
|
||||
|
||||
},
|
||||
draw: function(ease) {
|
||||
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
this.scale.draw(easingDecimal);
|
||||
|
||||
// reverse for-loop for proper stacking
|
||||
for (var i = this.data.datasets.length - 1; i >= 0; i--) {
|
||||
|
||||
var dataset = this.data.datasets[i];
|
||||
var datasetIndex = i;
|
||||
|
||||
// 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();
|
||||
});
|
||||
}
|
||||
|
||||
// Finally draw the tooltip
|
||||
this.tooltip.transition(easingDecimal).draw();
|
||||
},
|
||||
events: function(e) {
|
||||
|
||||
// If exiting chart
|
||||
if (e.type == 'mouseout') {
|
||||
@@ -155,19 +381,32 @@
|
||||
this.options.onHover.call(this, this.active);
|
||||
}
|
||||
|
||||
if (e.type == 'mouseup' || e.type == 'click') {
|
||||
if (this.options.onClick) {
|
||||
this.options.onClick.call(this, e, this.active);
|
||||
}
|
||||
}
|
||||
|
||||
var dataset;
|
||||
// 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;
|
||||
dataset = this.data.datasets[this.lastActive[0]._datasetIndex];
|
||||
|
||||
this.lastActive[0].radius = dataset.pointRadius;
|
||||
this.lastActive[0].backgroundColor = dataset.pointBackgroundColor;
|
||||
this.lastActive[0].borderColor = dataset.pointBorderColor;
|
||||
this.lastActive[0].borderWidth = dataset.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;
|
||||
dataset = this.data.datasets[this.lastActive[i]._datasetIndex];
|
||||
|
||||
this.lastActive[i].radius = dataset.pointRadius;
|
||||
this.lastActive[i].backgroundColor = dataset.pointBackgroundColor;
|
||||
this.lastActive[i].borderColor = dataset.pointBorderColor;
|
||||
this.lastActive[i].borderWidth = dataset.pointBorderWidth;
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
@@ -181,15 +420,21 @@
|
||||
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;
|
||||
dataset = this.data.datasets[this.active[0]._datasetIndex];
|
||||
|
||||
this.active[0].radius = dataset.pointHoverRadius || dataset.pointRadius + 2;
|
||||
this.active[0].backgroundColor = dataset.pointHoverBackgroundColor || helpers.color(dataset.pointBackgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[0].borderColor = dataset.pointHoverBorderColor || helpers.color(dataset.pointBorderColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[0].borderWidth = dataset.pointHoverBorderWidth || dataset.pointBorderWidth + 2;
|
||||
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;
|
||||
dataset = this.data.datasets[this.active[i]._datasetIndex];
|
||||
|
||||
this.active[i].radius = dataset.pointHoverRadius || dataset.pointRadius + 2;
|
||||
this.active[i].backgroundColor = dataset.pointHoverBackgroundColor || helpers.color(dataset.pointBackgroundColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[i].borderColor = dataset.pointHoverBorderColor || helpers.color(dataset.pointBorderColor).saturate(0.5).darken(0.35).rgbString();
|
||||
this.active[i].borderWidth = dataset.pointHoverBorderWidth || dataset.pointBorderWidth + 2;
|
||||
}
|
||||
break;
|
||||
case 'dataset':
|
||||
@@ -248,175 +493,7 @@
|
||||
// Remember Last Active
|
||||
this.lastActive = this.active;
|
||||
return this;
|
||||
|
||||
},
|
||||
update: function() {
|
||||
|
||||
this.scale.update();
|
||||
|
||||
// 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.scale.startPoint,
|
||||
scaleBottom: this.scale.endPoint,
|
||||
_points: dataset.metaData,
|
||||
_datasetIndex: datasetIndex,
|
||||
});
|
||||
dataset.metaDataset.pivot();
|
||||
});
|
||||
|
||||
// Update the points
|
||||
this.eachElement(function(point, index, dataset, datasetIndex) {
|
||||
helpers.extend(point, {
|
||||
x: this.scale.calculateX(index),
|
||||
y: this.scale.calculateY(this.data.datasets[datasetIndex].data[index]),
|
||||
value: this.data.datasets[datasetIndex].data[index],
|
||||
label: this.data.labels[index],
|
||||
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.scale.endPoint) {
|
||||
point.controlPointNextY = this.scale.endPoint;
|
||||
} else if (controlPoints.next.y < this.scale.startPoint) {
|
||||
point.controlPointNextY = this.scale.startPoint;
|
||||
} 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;
|
||||
} else {
|
||||
point.controlPointPreviousY = controlPoints.previous.y;
|
||||
}
|
||||
// Now pivot the point for animation
|
||||
point.pivot();
|
||||
}, this);
|
||||
|
||||
this.render();
|
||||
},
|
||||
buildScale: function(labels) {
|
||||
var self = this;
|
||||
|
||||
var dataTotal = function() {
|
||||
var values = [];
|
||||
self.eachValue(function(value) {
|
||||
values.push(value);
|
||||
});
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
var scaleOptions = {
|
||||
templateString: this.options.scaleLabel,
|
||||
height: this.chart.height,
|
||||
width: this.chart.width,
|
||||
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);
|
||||
},
|
||||
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
|
||||
};
|
||||
|
||||
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 Chart.Scale(scaleOptions);
|
||||
},
|
||||
redraw: function() {
|
||||
|
||||
},
|
||||
draw: function(ease) {
|
||||
|
||||
var easingDecimal = ease || 1;
|
||||
this.clear();
|
||||
|
||||
this.scale.draw(easingDecimal);
|
||||
|
||||
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();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user