RuoYi/infosouth-admin/src/main/resources/static/oneself/chartjs/Chart.PieceLabel.js

313 lines
10 KiB
JavaScript
Raw Normal View History

2020-03-04 18:46:56 +08:00
/**
* [Chart.PieceLabel.js]{@link https://github.com/emn178/Chart.PieceLabel.js}
*
* @version 0.9.0
* @author Chen, Yi-Cyuan [emn178@gmail.com]
* @copyright Chen, Yi-Cyuan 2017
* @license MIT
*/
(function () {
if (typeof Chart === 'undefined') {
console.warn('Can not find Chart object.');
return;
}
function PieceLabel() {
this.drawDataset = this.drawDataset.bind(this);
}
PieceLabel.prototype.beforeDatasetsUpdate = function (chartInstance) {
if (this.parseOptions(chartInstance) && this.position === 'outside') {
var padding = this.fontSize * 1.5 + 2;
chartInstance.chartArea.top += padding;
chartInstance.chartArea.bottom -= padding;
}
};
PieceLabel.prototype.afterDatasetsDraw = function (chartInstance) {
if (!this.parseOptions(chartInstance)) {
return;
}
this.labelBounds = [];
chartInstance.config.data.datasets.forEach(this.drawDataset);
};
PieceLabel.prototype.drawDataset = function (dataset) {
var ctx = this.ctx;
var chartInstance = this.chartInstance;
var meta = dataset._meta[Object.keys(dataset._meta)[0]];
var totalPercentage = 0;
for (var i = 0; i < meta.data.length; i++) {
var element = meta.data[i],
view = element._view, text;
//Skips label creation if value is zero and showZero is set
if (view.circumference === 0 && !this.showZero) {
continue;
}
switch (this.render) {
case 'value':
var value = dataset.data[i];
if (this.format) {
value = this.format(value);
}
text = value.toString();
break;
case 'label':
text = chartInstance.config.data.labels[i];
break;
case 'image':
text = this.images[i] ? this.loadImage(this.images[i]) : '';
break;
case 'percentage':
default:
var percentage = view.circumference / this.options.circumference * 100;
percentage = parseFloat(percentage.toFixed(this.precision));
if (!this.showActualPercentages) {
totalPercentage += percentage;
if (totalPercentage > 100) {
percentage -= totalPercentage - 100;
// After adjusting the percentage, need to trim the numbers after decimal points again, otherwise it may not show
// on chart due to very long number after decimal point.
percentage = parseFloat(percentage.toFixed(this.precision));
}
}
text = percentage + '%';
break;
}
if (typeof this.render === 'function') {
text = this.render({
label: chartInstance.config.data.labels[i],
value: dataset.data[i],
percentage: percentage,
dataset: dataset,
index: i
});
if (typeof text === 'object') {
text = this.loadImage(text);
}
}
if (!text) {
return;
}
ctx.save();
ctx.beginPath();
ctx.font = Chart.helpers.fontString(this.fontSize, this.fontStyle, this.fontFamily);
var position, innerRadius, arcOffset;
if (this.position === 'outside' ||
this.position === 'border' && chartInstance.config.type === 'pie') {
innerRadius = view.outerRadius / 2;
var rangeFromCentre, offset = this.fontSize + 2,
centreAngle = view.startAngle + ((view.endAngle - view.startAngle) / 2);
if (this.position === 'border') {
rangeFromCentre = (view.outerRadius - innerRadius) / 2 + innerRadius;
} else if (this.position === 'outside') {
rangeFromCentre = (view.outerRadius - innerRadius) + innerRadius + offset;
}
position = {
x: view.x + (Math.cos(centreAngle) * rangeFromCentre),
y: view.y + (Math.sin(centreAngle) * rangeFromCentre)
};
if (this.position === 'outside') {
if (position.x < view.x) {
position.x -= offset;
} else {
position.x += offset;
}
arcOffset = view.outerRadius + offset;
}
} else {
innerRadius = view.innerRadius;
position = element.tooltipPosition();
}
var fontColor = this.fontColor;
if (typeof fontColor === 'function') {
fontColor = fontColor({
label: chartInstance.config.data.labels[i],
value: dataset.data[i],
percentage: percentage,
text: text,
backgroundColor: dataset.backgroundColor[i],
dataset: dataset,
index: i
});
} else if (typeof fontColor !== 'string') {
fontColor = fontColor[i] || this.options.defaultFontColor;
}
if (this.arc) {
if (!arcOffset) {
arcOffset = (innerRadius + view.outerRadius) / 2;
}
ctx.fillStyle = fontColor;
ctx.textBaseline = 'middle';
this.drawArcText(text, arcOffset, view, this.overlap);
} else {
var drawable, mertrics = this.measureText(text),
left = position.x - mertrics.width / 2,
right = position.x + mertrics.width / 2,
top = position.y - this.fontSize / 2,
bottom = position.y + this.fontSize / 2;
if (this.overlap) {
drawable = true;
} else if (this.position === 'outside') {
drawable = this.checkTextBound(left, right, top, bottom);
} else {
drawable = element.inRange(left, top) && element.inRange(left, bottom) &&
element.inRange(right, top) && element.inRange(right, bottom);
}
if (drawable) {
this.fillText(text, position, fontColor);
}
}
ctx.restore();
}
};
PieceLabel.prototype.parseOptions = function (chartInstance) {
var pieceLabel = chartInstance.options.pieceLabel;
if (pieceLabel) {
this.chartInstance = chartInstance;
this.ctx = chartInstance.chart.ctx;
this.options = chartInstance.config.options;
this.render = pieceLabel.render || pieceLabel.mode;
this.position = pieceLabel.position || 'default';
this.arc = pieceLabel.arc;
this.format = pieceLabel.format;
this.precision = pieceLabel.precision || 0;
this.fontSize = pieceLabel.fontSize || this.options.defaultFontSize;
this.fontColor = pieceLabel.fontColor || this.options.defaultFontColor;
this.fontStyle = pieceLabel.fontStyle || this.options.defaultFontStyle;
this.fontFamily = pieceLabel.fontFamily || this.options.defaultFontFamily;
this.hasTooltip = chartInstance.tooltip._active && chartInstance.tooltip._active.length;
this.showZero = pieceLabel.showZero;
this.overlap = pieceLabel.overlap;
this.images = pieceLabel.images || [];
this.showActualPercentages = pieceLabel.showActualPercentages || false;
return true;
} else {
return false;
}
};
PieceLabel.prototype.checkTextBound = function (left, right, top, bottom) {
var labelBounds = this.labelBounds;
for (var i = 0;i < labelBounds.length;++i) {
var bound = labelBounds[i];
var potins = [
[left, top],
[left, bottom],
[right, top],
[right, bottom]
];
for (var j = 0;j < potins.length;++j) {
var x = potins[j][0];
var y = potins[j][1];
if (x >= bound.left && x <= bound.right && y >= bound.top && y <= bound.bottom) {
return false;
}
}
potins = [
[bound.left, bound.top],
[bound.left, bound.bottom],
[bound.right, bound.top],
[bound.right, bound.bottom]
];
for (var j = 0;j < potins.length;++j) {
var x = potins[j][0];
var y = potins[j][1];
if (x >= left && x <= right && y >= top && y <= bottom) {
return false;
}
}
}
labelBounds.push({
left: left,
right: right,
top: top,
bottom: bottom
});
return true;
};
PieceLabel.prototype.measureText = function (text) {
if (typeof text === 'object') {
return { width: text.width, height: text.height };
} else {
return this.ctx.measureText(text);
}
};
PieceLabel.prototype.fillText = function (text, position, fontColor) {
var ctx = this.ctx;
if (typeof text === 'object') {
ctx.drawImage(text, position.x - text.width / 2, position.y - text.height / 2, text.width, text.height);
} else {
ctx.fillStyle = fontColor;
ctx.textBaseline = 'top';
ctx.textAlign = 'center';
ctx.fillText(text, position.x, position.y - this.fontSize / 2);
}
};
PieceLabel.prototype.loadImage = function (obj) {
var image = new Image();
image.src = obj.src;
image.width = obj.width;
image.height = obj.height;
return image;
};
PieceLabel.prototype.drawArcText = function (str, radius, view, overlap) {
var ctx = this.ctx,
centerX = view.x,
centerY = view.y,
startAngle = view.startAngle,
endAngle = view.endAngle;
ctx.save();
ctx.translate(centerX, centerY);
var angleSize = endAngle - startAngle;
startAngle += Math.PI / 2;
endAngle += Math.PI / 2;
var origStartAngle = startAngle;
var mertrics = this.measureText(str);
startAngle += (endAngle - (mertrics.width / radius + startAngle)) / 2;
if (!overlap && endAngle - startAngle > angleSize) {
ctx.restore();
return;
}
if (typeof str === 'string') {
ctx.rotate(startAngle);
for (var i = 0; i < str.length; i++) {
var char = str.charAt(i);
mertrics = ctx.measureText(char);
ctx.save();
ctx.translate(0, -1 * radius);
ctx.fillText(char, 0, 0);
ctx.restore();
ctx.rotate(mertrics.width / radius);
}
} else {
ctx.rotate((origStartAngle + endAngle) / 2);
ctx.translate(0, -1 * radius);
this.fillText(str, { x: 0, y: 0 });
}
ctx.restore();
};
Chart.pluginService.register({
beforeInit: function(chartInstance) {
chartInstance.pieceLabel = new PieceLabel();
},
beforeDatasetsUpdate: function (chartInstance) {
chartInstance.pieceLabel.beforeDatasetsUpdate(chartInstance);
},
afterDatasetsDraw: function (chartInstance) {
chartInstance.pieceLabel.afterDatasetsDraw(chartInstance);
}
});
})();