mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-07 16:56:50 +01:00
217 lines
5.3 KiB
JavaScript
217 lines
5.3 KiB
JavaScript
import DatasetController from '../core/core.datasetController';
|
|
import {toRadians, PI} from '../helpers/index';
|
|
import {formatNumber} from '../helpers/helpers.intl';
|
|
|
|
export default class PolarAreaController extends DatasetController {
|
|
|
|
constructor(chart, datasetIndex) {
|
|
super(chart, datasetIndex);
|
|
|
|
this.innerRadius = undefined;
|
|
this.outerRadius = undefined;
|
|
}
|
|
|
|
getLabelAndValue(index) {
|
|
const me = this;
|
|
const meta = me._cachedMeta;
|
|
const chart = me.chart;
|
|
const labels = chart.data.labels || [];
|
|
const value = formatNumber(meta._parsed[index].r, chart.options.locale);
|
|
|
|
return {
|
|
label: labels[index] || '',
|
|
value,
|
|
};
|
|
}
|
|
|
|
update(mode) {
|
|
const arcs = this._cachedMeta.data;
|
|
|
|
this._updateRadius();
|
|
this.updateElements(arcs, 0, arcs.length, mode);
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_updateRadius() {
|
|
const me = this;
|
|
const chart = me.chart;
|
|
const chartArea = chart.chartArea;
|
|
const opts = chart.options;
|
|
const minSize = Math.min(chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
|
|
|
|
const outerRadius = Math.max(minSize / 2, 0);
|
|
const innerRadius = Math.max(opts.cutoutPercentage ? (outerRadius / 100) * (opts.cutoutPercentage) : 1, 0);
|
|
const radiusLength = (outerRadius - innerRadius) / chart.getVisibleDatasetCount();
|
|
|
|
me.outerRadius = outerRadius - (radiusLength * me.index);
|
|
me.innerRadius = me.outerRadius - radiusLength;
|
|
}
|
|
|
|
updateElements(arcs, start, count, mode) {
|
|
const me = this;
|
|
const reset = mode === 'reset';
|
|
const chart = me.chart;
|
|
const dataset = me.getDataset();
|
|
const opts = chart.options;
|
|
const animationOpts = opts.animation;
|
|
const scale = me._cachedMeta.rScale;
|
|
const centerX = scale.xCenter;
|
|
const centerY = scale.yCenter;
|
|
const datasetStartAngle = scale.getIndexAngle(0) - 0.5 * PI;
|
|
let angle = datasetStartAngle;
|
|
let i;
|
|
|
|
const defaultAngle = 360 / me.countVisibleElements();
|
|
|
|
for (i = 0; i < start; ++i) {
|
|
angle += me._computeAngle(i, mode, defaultAngle);
|
|
}
|
|
for (i = start; i < start + count; i++) {
|
|
const arc = arcs[i];
|
|
let startAngle = angle;
|
|
let endAngle = angle + me._computeAngle(i, mode, defaultAngle);
|
|
let outerRadius = chart.getDataVisibility(i) ? scale.getDistanceFromCenterForValue(dataset.data[i]) : 0;
|
|
angle = endAngle;
|
|
|
|
if (reset) {
|
|
if (animationOpts.animateScale) {
|
|
outerRadius = 0;
|
|
}
|
|
if (animationOpts.animateRotate) {
|
|
startAngle = endAngle = datasetStartAngle;
|
|
}
|
|
}
|
|
|
|
const properties = {
|
|
x: centerX,
|
|
y: centerY,
|
|
innerRadius: 0,
|
|
outerRadius,
|
|
startAngle,
|
|
endAngle,
|
|
options: me.resolveDataElementOptions(i, arc.active ? 'active' : mode)
|
|
};
|
|
|
|
me.updateElement(arc, i, properties, mode);
|
|
}
|
|
}
|
|
|
|
countVisibleElements() {
|
|
const dataset = this.getDataset();
|
|
const meta = this._cachedMeta;
|
|
let count = 0;
|
|
|
|
meta.data.forEach((element, index) => {
|
|
if (!isNaN(dataset.data[index]) && this.chart.getDataVisibility(index)) {
|
|
count++;
|
|
}
|
|
});
|
|
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
_computeAngle(index, mode, defaultAngle) {
|
|
return this.chart.getDataVisibility(index)
|
|
? toRadians(this.resolveDataElementOptions(index, mode).angle || defaultAngle)
|
|
: 0;
|
|
}
|
|
}
|
|
|
|
PolarAreaController.id = 'polarArea';
|
|
|
|
/**
|
|
* @type {any}
|
|
*/
|
|
PolarAreaController.defaults = {
|
|
dataElementType: 'arc',
|
|
animation: {
|
|
animateRotate: true,
|
|
animateScale: true
|
|
},
|
|
animations: {
|
|
numbers: {
|
|
type: 'number',
|
|
properties: ['x', 'y', 'startAngle', 'endAngle', 'innerRadius', 'outerRadius']
|
|
},
|
|
},
|
|
indexAxis: 'r',
|
|
startAngle: 0,
|
|
};
|
|
|
|
/**
|
|
* @type {any}
|
|
*/
|
|
PolarAreaController.overrides = {
|
|
aspectRatio: 1,
|
|
|
|
plugins: {
|
|
legend: {
|
|
labels: {
|
|
generateLabels(chart) {
|
|
const data = chart.data;
|
|
if (data.labels.length && data.datasets.length) {
|
|
const {labels: {pointStyle}} = chart.legend.options;
|
|
|
|
return data.labels.map((label, i) => {
|
|
const meta = chart.getDatasetMeta(0);
|
|
const style = meta.controller.getStyle(i);
|
|
|
|
return {
|
|
text: label,
|
|
fillStyle: style.backgroundColor,
|
|
strokeStyle: style.borderColor,
|
|
lineWidth: style.borderWidth,
|
|
pointStyle: pointStyle,
|
|
hidden: !chart.getDataVisibility(i),
|
|
|
|
// Extra data used for toggling the correct item
|
|
index: i
|
|
};
|
|
});
|
|
}
|
|
return [];
|
|
}
|
|
},
|
|
|
|
onClick(e, legendItem, legend) {
|
|
legend.chart.toggleDataVisibility(legendItem.index);
|
|
legend.chart.update();
|
|
}
|
|
},
|
|
|
|
// Need to override these to give a nice default
|
|
tooltip: {
|
|
callbacks: {
|
|
title() {
|
|
return '';
|
|
},
|
|
label(context) {
|
|
return context.chart.data.labels[context.dataIndex] + ': ' + context.formattedValue;
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
scales: {
|
|
r: {
|
|
type: 'radialLinear',
|
|
angleLines: {
|
|
display: false
|
|
},
|
|
beginAtZero: true,
|
|
grid: {
|
|
circular: true
|
|
},
|
|
pointLabels: {
|
|
display: false
|
|
},
|
|
startAngle: 0
|
|
}
|
|
}
|
|
};
|