Chart.js/src/controllers/controller.doughnut.js

289 lines
12 KiB
JavaScript
Raw Normal View History

"use strict";
2015-06-13 16:15:21 +02:00
module.exports = function(Chart) {
var helpers = Chart.helpers;
2015-06-13 16:15:21 +02:00
Chart.defaults.doughnut = {
2015-06-13 16:15:21 +02:00
animation: {
//Boolean - Whether we animate the rotation of the Doughnut
animateRotate: true,
//Boolean - Whether we animate scaling the Doughnut from the centre
animateScale: false
2015-06-13 16:15:21 +02:00
},
aspectRatio: 1,
2015-06-13 16:15:21 +02:00
hover: {
mode: 'single'
},
legendCallback: function(chart) {
var text = [];
text.push('<ul class="' + chart.id + '-legend">');
if (chart.data.datasets.length) {
for (var i = 0; i < chart.data.datasets[0].data.length; ++i) {
text.push('<li><span style="background-color:' + chart.data.datasets[0].backgroundColor[i] + '"></span>');
if (chart.data.labels[i]) {
text.push(chart.data.labels[i]);
}
text.push('</li>');
}
}
text.push('</ul>');
return text.join("");
},
legend: {
labels: {
generateLabels: function(data) {
2016-02-28 23:51:06 +01:00
if (data.labels.length && data.datasets.length) {
2016-02-28 23:51:06 +01:00
return data.labels.map(function(label, i) {
var ds = data.datasets[0];
var arc = ds.metaData[i];
var fill = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(ds.backgroundColor, i, this.chart.options.elements.arc.backgroundColor);
var stroke = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(ds.borderColor, i, this.chart.options.elements.arc.borderColor);
var bw = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(ds.borderWidth, i, this.chart.options.elements.arc.borderWidth);
2016-02-28 23:51:06 +01:00
return {
text: label,
fillStyle: fill,
strokeStyle: stroke,
lineWidth: bw,
2016-02-28 23:51:06 +01:00
hidden: isNaN(data.datasets[0].data[i]),
2016-02-28 23:51:06 +01:00
// Extra data used for toggling the correct item
index: i
};
}, this);
2016-02-28 23:51:06 +01:00
} else {
return [];
}
}
2015-12-08 03:23:20 +01:00
},
onClick: function(e, legendItem) {
helpers.each(this.chart.data.datasets, function(dataset) {
dataset.metaHiddenData = dataset.metaHiddenData || [];
var idx = legendItem.index;
if (!isNaN(dataset.data[idx])) {
dataset.metaHiddenData[idx] = dataset.data[idx];
dataset.data[idx] = NaN;
} else if (!isNaN(dataset.metaHiddenData[idx])) {
dataset.data[idx] = dataset.metaHiddenData[idx];
}
});
this.chart.update();
}
},
2015-06-13 16:15:21 +02:00
//The percentage of the chart that we cut out of the middle.
cutoutPercentage: 50,
2015-10-31 01:48:31 +01:00
//The rotation of the chart, where the first data arc begins.
rotation: Math.PI * -0.5,
//The total circumference of the chart.
circumference: Math.PI * 2.0,
2015-10-31 01:48:31 +01:00
// Need to override these to give a nice default
tooltips: {
callbacks: {
title: function() {
return '';
},
2015-10-31 01:48:31 +01:00
label: function(tooltipItem, data) {
return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
}
}
}
2015-06-13 16:15:21 +02:00
};
2015-06-16 06:47:20 +02:00
Chart.defaults.pie = helpers.clone(Chart.defaults.doughnut);
helpers.extend(Chart.defaults.pie, {
cutoutPercentage: 0
});
Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
linkScales: function() {
// no scales for doughnut
},
addElements: function() {
this.getDataset().metaData = this.getDataset().metaData || [];
helpers.each(this.getDataset().data, function(value, index) {
this.getDataset().metaData[index] = this.getDataset().metaData[index] || new Chart.elements.Arc({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
}, this);
},
addElementAndReset: function(index, colorForNewElement) {
this.getDataset().metaData = this.getDataset().metaData || [];
var arc = new Chart.elements.Arc({
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index
});
2015-08-30 20:56:48 +02:00
if (colorForNewElement && helpers.isArray(this.getDataset().backgroundColor)) {
this.getDataset().backgroundColor.splice(index, 0, colorForNewElement);
}
// Reset the point
this.updateElement(arc, index, true);
// Add to the points array
this.getDataset().metaData.splice(index, 0, arc);
},
getVisibleDatasetCount: function getVisibleDatasetCount() {
return helpers.where(this.chart.data.datasets, function(ds) {
return helpers.isDatasetVisible(ds);
}).length;
},
// Get index of the dataset in relation to the visible datasets. This allows determining the inner and outer radius correctly
getRingIndex: function getRingIndex(datasetIndex) {
var ringIndex = 0;
for (var j = 0; j < datasetIndex; ++j) {
if (helpers.isDatasetVisible(this.chart.data.datasets[j])) {
++ringIndex;
}
}
return ringIndex;
},
update: function update(reset) {
var availableWidth = this.chart.chartArea.right - this.chart.chartArea.left - this.chart.options.elements.arc.borderWidth;
var availableHeight = this.chart.chartArea.bottom - this.chart.chartArea.top - this.chart.options.elements.arc.borderWidth;
var minSize = Math.min(availableWidth, availableHeight);
var offset = {x: 0, y: 0};
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
if (this.chart.options.circumference < Math.PI * 2.0) {
var startAngle = this.chart.options.rotation % (Math.PI * 2.0);
startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
var endAngle = startAngle + this.chart.options.circumference;
var start = {x: Math.cos(startAngle), y: Math.sin(startAngle)};
var end = {x: Math.cos(endAngle), y: Math.sin(endAngle)};
var contains0 = (startAngle <= 0 && 0 <= endAngle) || (startAngle <= Math.PI * 2.0 && Math.PI * 2.0 <= endAngle);
var contains90 = (startAngle <= Math.PI * 0.5 && Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 2.5 && Math.PI * 2.5 <= endAngle);
var contains180 = (startAngle <= -Math.PI && -Math.PI <= endAngle) || (startAngle <= Math.PI && Math.PI <= endAngle);
var contains270 = (startAngle <= -Math.PI * 0.5 && -Math.PI * 0.5 <= endAngle) || (startAngle <= Math.PI * 1.5 && Math.PI * 1.5 <= endAngle);
var cutout = this.chart.options.cutoutPercentage / 100.0;
var min = {x: contains180 ? -1 : Math.min(start.x * (start.x < 0 ? 1 : cutout), end.x * (end.x < 0 ? 1 : cutout)), y: contains270 ? -1 : Math.min(start.y * (start.y < 0 ? 1 : cutout), end.y * (end.y < 0 ? 1 : cutout))};
var max = {x: contains0 ? 1 : Math.max(start.x * (start.x > 0 ? 1 : cutout), end.x * (end.x > 0 ? 1 : cutout)), y: contains90 ? 1 : Math.max(start.y * (start.y > 0 ? 1 : cutout), end.y * (end.y > 0 ? 1 : cutout))};
var size = {width: (max.x - min.x) * 0.5, height: (max.y - min.y) * 0.5};
minSize = Math.min(availableWidth / size.width, availableHeight / size.height);
offset = {x: (max.x + min.x) * -0.5, y: (max.y + min.y) * -0.5};
}
this.chart.outerRadius = Math.max(minSize / 2, 0);
this.chart.innerRadius = Math.max(this.chart.options.cutoutPercentage ? (this.chart.outerRadius / 100) * (this.chart.options.cutoutPercentage) : 1, 0);
this.chart.radiusLength = (this.chart.outerRadius - this.chart.innerRadius) / this.getVisibleDatasetCount();
this.chart.offsetX = offset.x * this.chart.outerRadius;
this.chart.offsetY = offset.y * this.chart.outerRadius;
this.getDataset().total = 0;
helpers.each(this.getDataset().data, function(value) {
2015-12-08 03:23:20 +01:00
if (!isNaN(value)) {
this.getDataset().total += Math.abs(value);
}
}, this);
this.outerRadius = this.chart.outerRadius - (this.chart.radiusLength * this.getRingIndex(this.index));
this.innerRadius = this.outerRadius - this.chart.radiusLength;
2015-08-30 20:56:48 +02:00
helpers.each(this.getDataset().metaData, function(arc, index) {
this.updateElement(arc, index, reset);
}, this);
},
updateElement: function(arc, index, reset) {
var centerX = (this.chart.chartArea.left + this.chart.chartArea.right) / 2;
var centerY = (this.chart.chartArea.top + this.chart.chartArea.bottom) / 2;
var startAngle = this.chart.options.rotation; // non reset case handled later
var endAngle = this.chart.options.rotation; // non reset case handled later
var circumference = reset && this.chart.options.animation.animateRotate ? 0 : this.calculateCircumference(this.getDataset().data[index]) * (this.chart.options.circumference / (2.0 * Math.PI));
var innerRadius = reset && this.chart.options.animation.animateScale ? 0 : this.innerRadius;
var outerRadius = reset && this.chart.options.animation.animateScale ? 0 : this.outerRadius;
helpers.extend(arc, {
// Utility
_chart: this.chart.chart,
_datasetIndex: this.index,
_index: index,
// Desired view properties
_model: {
x: centerX + this.chart.offsetX,
y: centerY + this.chart.offsetY,
startAngle: startAngle,
endAngle: endAngle,
circumference: circumference,
outerRadius: outerRadius,
innerRadius: innerRadius,
backgroundColor: arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor),
hoverBackgroundColor: arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().hoverBackgroundColor, index, this.chart.options.elements.arc.hoverBackgroundColor),
borderWidth: arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth),
borderColor: arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor),
label: helpers.getValueAtIndexOrDefault(this.getDataset().label, index, this.chart.data.labels[index])
}
});
// Set correct angles if not resetting
if (!reset) {
if (index === 0) {
arc._model.startAngle = this.chart.options.rotation;
} else {
arc._model.startAngle = this.getDataset().metaData[index - 1]._model.endAngle;
}
arc._model.endAngle = arc._model.startAngle + arc._model.circumference;
}
arc.pivot();
},
draw: function(ease) {
var easingDecimal = ease || 1;
helpers.each(this.getDataset().metaData, function(arc, index) {
arc.transition(easingDecimal).draw();
2016-01-14 18:17:14 +01:00
});
},
2015-06-16 06:43:24 +02:00
setHoverStyle: function(arc) {
var dataset = this.chart.data.datasets[arc._datasetIndex];
var index = arc._index;
2015-06-16 06:43:24 +02:00
arc._model.backgroundColor = arc.custom && arc.custom.hoverBackgroundColor ? arc.custom.hoverBackgroundColor : helpers.getValueAtIndexOrDefault(dataset.hoverBackgroundColor, index, helpers.color(arc._model.backgroundColor).saturate(0.5).darken(0.1).rgbString());
arc._model.borderColor = arc.custom && arc.custom.hoverBorderColor ? arc.custom.hoverBorderColor : helpers.getValueAtIndexOrDefault(dataset.hoverBorderColor, index, helpers.color(arc._model.borderColor).saturate(0.5).darken(0.1).rgbString());
2015-08-30 20:56:48 +02:00
arc._model.borderWidth = arc.custom && arc.custom.hoverBorderWidth ? arc.custom.hoverBorderWidth : helpers.getValueAtIndexOrDefault(dataset.hoverBorderWidth, index, arc._model.borderWidth);
},
2015-06-16 06:43:24 +02:00
removeHoverStyle: function(arc) {
var dataset = this.chart.data.datasets[arc._datasetIndex];
var index = arc._index;
arc._model.backgroundColor = arc.custom && arc.custom.backgroundColor ? arc.custom.backgroundColor : helpers.getValueAtIndexOrDefault(this.getDataset().backgroundColor, index, this.chart.options.elements.arc.backgroundColor);
arc._model.borderColor = arc.custom && arc.custom.borderColor ? arc.custom.borderColor : helpers.getValueAtIndexOrDefault(this.getDataset().borderColor, index, this.chart.options.elements.arc.borderColor);
arc._model.borderWidth = arc.custom && arc.custom.borderWidth ? arc.custom.borderWidth : helpers.getValueAtIndexOrDefault(this.getDataset().borderWidth, index, this.chart.options.elements.arc.borderWidth);
},
calculateCircumference: function(value) {
2015-12-08 03:23:20 +01:00
if (this.getDataset().total > 0 && !isNaN(value)) {
return (Math.PI * 2.0) * (value / this.getDataset().total);
} else {
return 0;
}
}
});
};