mirror of
https://github.com/chartjs/Chart.js.git
synced 2024-10-08 05:09:08 +02:00
5fae4dd305
This change allows for smaller minified code in the final version, resulting in a smaller file size. Some files had previously used _this, but that has been changed to me to keep consistency throughout the project.
270 lines
8.8 KiB
JavaScript
270 lines
8.8 KiB
JavaScript
"use strict";
|
|
|
|
module.exports = function(Chart) {
|
|
|
|
var helpers = Chart.helpers,
|
|
defaults = Chart.defaults;
|
|
|
|
defaults.doughnut = {
|
|
animation: {
|
|
//Boolean - Whether we animate the rotation of the Doughnut
|
|
animateRotate: true,
|
|
//Boolean - Whether we animate scaling the Doughnut from the centre
|
|
animateScale: false
|
|
},
|
|
aspectRatio: 1,
|
|
hover: {
|
|
mode: 'single'
|
|
},
|
|
legendCallback: function(chart) {
|
|
var text = [];
|
|
text.push('<ul class="' + chart.id + '-legend">');
|
|
|
|
var data = chart.data;
|
|
var datasets = data.datasets;
|
|
var labels = data.labels;
|
|
|
|
if (datasets.length) {
|
|
for (var i = 0; i < datasets[0].data.length; ++i) {
|
|
text.push('<li><span style="background-color:' + datasets[0].backgroundColor[i] + '"></span>');
|
|
if (labels[i]) {
|
|
text.push(labels[i]);
|
|
}
|
|
text.push('</li>');
|
|
}
|
|
}
|
|
|
|
text.push('</ul>');
|
|
return text.join("");
|
|
},
|
|
legend: {
|
|
labels: {
|
|
generateLabels: function(chart) {
|
|
var data = chart.data;
|
|
if (data.labels.length && data.datasets.length) {
|
|
return data.labels.map(function(label, i) {
|
|
var meta = chart.getDatasetMeta(0);
|
|
var ds = data.datasets[0];
|
|
var arc = meta.data[i];
|
|
var custom = arc.custom || {};
|
|
var getValueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
|
|
var arcOpts = chart.options.elements.arc;
|
|
var fill = custom.backgroundColor ? custom.backgroundColor : getValueAtIndexOrDefault(ds.backgroundColor, i, arcOpts.backgroundColor);
|
|
var stroke = custom.borderColor ? custom.borderColor : getValueAtIndexOrDefault(ds.borderColor, i, arcOpts.borderColor);
|
|
var bw = custom.borderWidth ? custom.borderWidth : getValueAtIndexOrDefault(ds.borderWidth, i, arcOpts.borderWidth);
|
|
|
|
return {
|
|
text: label,
|
|
fillStyle: fill,
|
|
strokeStyle: stroke,
|
|
lineWidth: bw,
|
|
hidden: isNaN(ds.data[i]) || meta.data[i].hidden,
|
|
|
|
// Extra data used for toggling the correct item
|
|
index: i
|
|
};
|
|
});
|
|
} else {
|
|
return [];
|
|
}
|
|
}
|
|
},
|
|
|
|
onClick: function(e, legendItem) {
|
|
var index = legendItem.index;
|
|
var chart = this.chart;
|
|
var i, ilen, meta;
|
|
|
|
for (i = 0, ilen = (chart.data.datasets || []).length; i < ilen; ++i) {
|
|
meta = chart.getDatasetMeta(i);
|
|
meta.data[index].hidden = !meta.data[index].hidden;
|
|
}
|
|
|
|
chart.update();
|
|
}
|
|
},
|
|
|
|
//The percentage of the chart that we cut out of the middle.
|
|
cutoutPercentage: 50,
|
|
|
|
//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,
|
|
|
|
// Need to override these to give a nice default
|
|
tooltips: {
|
|
callbacks: {
|
|
title: function() {
|
|
return '';
|
|
},
|
|
label: function(tooltipItem, data) {
|
|
return data.labels[tooltipItem.index] + ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
defaults.pie = helpers.clone(defaults.doughnut);
|
|
helpers.extend(defaults.pie, {
|
|
cutoutPercentage: 0
|
|
});
|
|
|
|
|
|
Chart.controllers.doughnut = Chart.controllers.pie = Chart.DatasetController.extend({
|
|
|
|
dataElementType: Chart.elements.Arc,
|
|
|
|
linkScales: helpers.noop,
|
|
|
|
// 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 (this.chart.isDatasetVisible(j)) {
|
|
++ringIndex;
|
|
}
|
|
}
|
|
|
|
return ringIndex;
|
|
},
|
|
|
|
update: function update(reset) {
|
|
var me = this;
|
|
var chart = me.chart,
|
|
chartArea = chart.chartArea,
|
|
opts = chart.options,
|
|
arcOpts = opts.elements.arc,
|
|
availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth,
|
|
availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth,
|
|
minSize = Math.min(availableWidth, availableHeight),
|
|
offset = {
|
|
x: 0,
|
|
y: 0
|
|
},
|
|
meta = me.getMeta(),
|
|
cutoutPercentage = opts.cutoutPercentage,
|
|
circumference = opts.circumference;
|
|
|
|
// If the chart's circumference isn't a full circle, calculate minSize as a ratio of the width/height of the arc
|
|
if (circumference < Math.PI * 2.0) {
|
|
var startAngle = opts.rotation % (Math.PI * 2.0);
|
|
startAngle += Math.PI * 2.0 * (startAngle >= Math.PI ? -1 : startAngle < -Math.PI ? 1 : 0);
|
|
var endAngle = startAngle + 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 = 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};
|
|
}
|
|
|
|
chart.outerRadius = Math.max(minSize / 2, 0);
|
|
chart.innerRadius = Math.max(cutoutPercentage ? (chart.outerRadius / 100) * (cutoutPercentage) : 1, 0);
|
|
chart.radiusLength = (chart.outerRadius - chart.innerRadius) / chart.getVisibleDatasetCount();
|
|
chart.offsetX = offset.x * chart.outerRadius;
|
|
chart.offsetY = offset.y * chart.outerRadius;
|
|
|
|
meta.total = me.calculateTotal();
|
|
|
|
me.outerRadius = chart.outerRadius - (chart.radiusLength * me.getRingIndex(me.index));
|
|
me.innerRadius = me.outerRadius - chart.radiusLength;
|
|
|
|
helpers.each(meta.data, function(arc, index) {
|
|
me.updateElement(arc, index, reset);
|
|
});
|
|
},
|
|
|
|
updateElement: function(arc, index, reset) {
|
|
var me = this;
|
|
var chart = me.chart,
|
|
chartArea = chart.chartArea,
|
|
opts = chart.options,
|
|
animationOpts = opts.animation,
|
|
arcOpts = opts.elements.arc,
|
|
centerX = (chartArea.left + chartArea.right) / 2,
|
|
centerY = (chartArea.top + chartArea.bottom) / 2,
|
|
startAngle = opts.rotation, // non reset case handled later
|
|
endAngle = opts.rotation, // non reset case handled later
|
|
dataset = me.getDataset(),
|
|
circumference = reset && animationOpts.animateRotate ? 0 : arc.hidden ? 0 : me.calculateCircumference(dataset.data[index]) * (opts.circumference / (2.0 * Math.PI)),
|
|
innerRadius = reset && animationOpts.animateScale ? 0 : me.innerRadius,
|
|
outerRadius = reset && animationOpts.animateScale ? 0 : me.outerRadius,
|
|
custom = arc.custom || {},
|
|
valueAtIndexOrDefault = helpers.getValueAtIndexOrDefault;
|
|
|
|
helpers.extend(arc, {
|
|
// Utility
|
|
_datasetIndex: me.index,
|
|
_index: index,
|
|
|
|
// Desired view properties
|
|
_model: {
|
|
x: centerX + chart.offsetX,
|
|
y: centerY + chart.offsetY,
|
|
startAngle: startAngle,
|
|
endAngle: endAngle,
|
|
circumference: circumference,
|
|
outerRadius: outerRadius,
|
|
innerRadius: innerRadius,
|
|
label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index])
|
|
}
|
|
});
|
|
|
|
var model = arc._model;
|
|
// Resets the visual styles
|
|
this.removeHoverStyle(arc);
|
|
|
|
// Set correct angles if not resetting
|
|
if (!reset || !animationOpts.animateRotate) {
|
|
if (index === 0) {
|
|
model.startAngle = opts.rotation;
|
|
} else {
|
|
model.startAngle = me.getMeta().data[index - 1]._model.endAngle;
|
|
}
|
|
|
|
model.endAngle = model.startAngle + model.circumference;
|
|
}
|
|
|
|
arc.pivot();
|
|
},
|
|
|
|
removeHoverStyle: function(arc) {
|
|
Chart.DatasetController.prototype.removeHoverStyle.call(this, arc, this.chart.options.elements.arc);
|
|
},
|
|
|
|
calculateTotal: function() {
|
|
var dataset = this.getDataset();
|
|
var meta = this.getMeta();
|
|
var total = 0;
|
|
var value;
|
|
|
|
helpers.each(meta.data, function(element, index) {
|
|
value = dataset.data[index];
|
|
if (!isNaN(value) && !element.hidden) {
|
|
total += Math.abs(value);
|
|
}
|
|
});
|
|
|
|
return total;
|
|
},
|
|
|
|
calculateCircumference: function(value) {
|
|
var total = this.getMeta().total;
|
|
if (total > 0 && !isNaN(value)) {
|
|
return (Math.PI * 2.0) * (value / total);
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
});
|
|
};
|