chartjs-chart-radial-gauge
Version:
Chart.js radial gauge
461 lines (385 loc) • 15 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('chart.js')) :
typeof define === 'function' && define.amd ? define(['chart.js'], factory) :
(global.ChartjsRadialGauge = factory(global.Chart));
}(this, (function (Chart) { 'use strict';
var Chart__default = 'default' in Chart ? Chart['default'] : Chart;
/**
* An arc element that supports rounded corners
*/
Chart.elements.RoundedArc = Chart.elements.Arc.extend({
draw: function draw() {
var ctx = this._chart.ctx;
var vm = this._view;
var startAngle = vm.startAngle,
endAngle = vm.endAngle;
var cornerRadius = (vm.outerRadius - vm.innerRadius) / 2;
var cornerX = (vm.outerRadius + vm.innerRadius) / 2;
// translate + rotate to make drawing the corners simpler
ctx.translate(vm.x, vm.y);
ctx.rotate(startAngle);
var angle = endAngle - startAngle;
ctx.beginPath();
if (vm.roundedCorners) {
ctx.arc(cornerX, 0, cornerRadius, Math.PI, 0);
}
ctx.arc(0, 0, vm.outerRadius, 0, angle);
var x = cornerX * Math.cos(angle);
var y = cornerX * Math.sin(angle);
if (vm.roundedCorners) {
ctx.arc(x, y, cornerRadius, angle, angle + Math.PI);
}
ctx.arc(0, 0, vm.innerRadius, angle, 0, true);
ctx.closePath();
ctx.rotate(-startAngle);
ctx.translate(-vm.x, -vm.y);
ctx.strokeStyle = vm.borderColor;
ctx.lineWidth = vm.borderWidth;
ctx.fillStyle = vm.backgroundColor;
ctx.fill();
ctx.lineJoin = 'bevel';
if (vm.borderWidth) {
ctx.stroke();
}
}
});
Chart.elements.RoundedArc;
var slicedToArray = function () {
function sliceIterator(arr, i) {
var _arr = [];
var _n = true;
var _d = false;
var _e = undefined;
try {
for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"]) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
return function (arr, i) {
if (Array.isArray(arr)) {
return arr;
} else if (Symbol.iterator in Object(arr)) {
return sliceIterator(arr, i);
} else {
throw new TypeError("Invalid attempt to destructure non-iterable instance");
}
};
}();
var helpers = Chart__default.helpers;
/**
* Controller for the radialGauge chart type
*/
Chart__default.defaults._set('radialGauge', {
animation: {
// Boolean - Whether we animate the rotation of the radialGauge
animateRotate: true,
// Boolean - Whether we animate scaling the radialGauge from the centre
animateScale: true
},
// The percentage of the chart that is the center area
centerPercentage: 80,
// The rotation for the start of the metric's arc
rotation: -Math.PI / 2,
// the color of the radial gauge's track
trackColor: 'rgb(204, 221, 238)',
// whether arc for the gauge should have rounded corners
roundedCorners: true,
// center value options
centerArea: {
// whether to display the center text value
displayText: true,
// font for the center text
fontFamily: null,
// color of the center text
fontColor: null,
// the size of the center text
fontSize: null,
// padding around the center area
padding: 4,
// an image to use for the center background
backgroundImage: null,
// a color to use for the center background
backgroundColor: null,
// the text to display in the center
// this could be a string or a callback that returns a string
// if a callback is provided it will be called with (value, options)
text: null
},
hover: {
mode: 'single'
},
legend: {
display: false
},
// the domain of the metric
domain: [0, 100],
tooltips: {
callbacks: {
title: function title() {
return '';
},
label: function label(tooltipItem, data) {
var dataLabel = data.labels[tooltipItem.index];
var value = ': ' + data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index];
dataLabel += value;
return dataLabel;
}
}
}
});
// eslint-disable-next-line no-shadow
var RadialGaugeController = (function (Chart$$1) {
Chart$$1.controllers.radialGauge = Chart$$1.DatasetController.extend({
dataElementType: Chart$$1.elements.RoundedArc,
linkScales: helpers.noop,
draw: function draw() {
this.drawTrack();
this.drawCenterArea();
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
Chart$$1.DatasetController.prototype.draw.apply(this, args);
},
drawTrack: function drawTrack() {
new Chart$$1.elements.Arc({
_view: {
backgroundColor: this.chart.options.trackColor,
borderColor: this.chart.options.trackColor,
startAngle: 0,
endAngle: Math.PI * 2,
x: this.centerX,
y: this.centerY,
innerRadius: this.innerRadius,
outerRadius: this.outerRadius,
borderWidth: this.borderWidth
},
_chart: this.chart
}).draw();
},
drawCenterArea: function drawCenterArea() {
var ctx = this.chart.ctx;
var drawInfo = {
ctx: ctx,
value: Math.ceil(this.getMeta().data[0]._view.value),
radius: this.innerRadius,
options: this.chart.options.centerArea
};
ctx.save();
try {
ctx.translate(this.centerX, this.centerY);
if (drawInfo.options.draw) {
drawInfo.options.draw(drawInfo);
return;
}
if (drawInfo.options.backgroundColor) {
this.drawCenterBackground(drawInfo);
}
if (drawInfo.options.backgroundImage) {
this.drawCenterImage(drawInfo);
}
if (drawInfo.options.displayText) {
this.drawCenterText(drawInfo);
}
} finally {
ctx.restore();
}
},
drawCenterBackground: function drawCenterBackground(_ref) {
var options = _ref.options,
radius = _ref.radius,
ctx = _ref.ctx;
var bgRadius = radius - options.padding;
ctx.beginPath();
ctx.arc(0, 0, bgRadius, 0, Math.PI * 2);
ctx.closePath();
ctx.fillStyle = options.backgroundColor;
ctx.fill();
},
drawCenterImage: function drawCenterImage(_ref2) {
var radius = _ref2.radius,
options = _ref2.options,
ctx = _ref2.ctx;
var imageRadius = radius - options.padding;
ctx.beginPath();
ctx.arc(0, 0, imageRadius, 0, Math.PI * 2, true);
ctx.closePath();
ctx.clip();
ctx.drawImage(options.backgroundImage, -imageRadius, -imageRadius, 2 * imageRadius, 2 * imageRadius);
},
wrapText: function wrapText(context, text, x, y, maxWidth, lineHeight) {
var words = text.split(' ');
var line = '';
for (var n = 0; n < words.length; n += 1) {
var testLine = line.concat(words[n], ' ');
var metrics = context.measureText(testLine);
var testWidth = metrics.width;
if (testWidth > maxWidth && n > 0) {
context.textAlign = 'center';
context.fillText(line, x, y);
line = words[n].concat(' ');
y += lineHeight;
} else {
line = testLine;
}
}
context.textAlign = 'center';
context.fillText(line, x, y);
},
drawCenterText: function drawCenterText(_ref3) {
var options = _ref3.options,
value = _ref3.value;
var fontSize = options.fontSize || (this.innerRadius / 50).toFixed(2) + 'em';
if (typeof fontSize === 'number') {
fontSize = fontSize + 'px';
}
var fontFamily = options.fontFamily || Chart$$1.defaults.global.defaultFontFamily;
var color = options.fontColor || Chart$$1.defaults.global.defaultFontColor;
var text = typeof options.text === 'function' ? options.text(value, options) : options.text;
text = text || '' + value;
this.chart.ctx.font = fontSize + ' ' + fontFamily;
this.chart.ctx.fillStyle = color;
this.chart.ctx.textBaseline = 'middle';
var textWidth = this.chart.ctx.measureText(text).width;
var textX = Math.round(-textWidth / 2);
// Only display the text if it fits. The Radius is half the width of the
// circle * 2 is the diameter and 0.8 is 80% of that.
if (textWidth < 2 * this.innerRadius * 0.8) {
this.chart.ctx.fillText(text, textX, 0);
}
// Draw any sub-text.
text = options.subText;
if (text) {
// The font-size is half of of the calculated size above.
fontSize = (this.innerRadius / 100).toFixed(2) + 'em';
// Set the size of the font.
this.chart.ctx.font = fontSize + ' ' + fontFamily;
// Calculate the width of the sub-text.
textWidth = this.chart.ctx.measureText(text).width;
// Calculate the x-coordinate of the text.
textX = Math.round(-textWidth / 2);
// Draw the sub-text 30% below the main text. Wrap the text and center
// it.
this.wrapText(this.chart.ctx, text, 0, this.innerRadius * 0.3, this.innerRadius * 2 * 0.8, this.innerRadius / 100 + this.innerRadius * 0.2);
}
},
update: function update(reset) {
var _this = this;
var chart = this.chart;
var chartArea = chart.chartArea;
var opts = chart.options;
var arcOpts = opts.elements.arc;
var availableWidth = chartArea.right - chartArea.left - arcOpts.borderWidth;
var availableHeight = chartArea.bottom - chartArea.top - arcOpts.borderWidth;
var availableSize = Math.min(availableWidth, availableHeight);
var meta = this.getMeta();
var centerPercentage = opts.centerPercentage;
this.borderWidth = this.getMaxBorderWidth(meta.data);
this.outerRadius = Math.max((availableSize - this.borderWidth) / 2, 0);
this.innerRadius = Math.max(centerPercentage ? this.outerRadius / 100 * centerPercentage : 0, 0);
meta.total = this.getMetricValue();
this.centerX = (chartArea.left + chartArea.right) / 2;
this.centerY = (chartArea.top + chartArea.bottom) / 2;
helpers.each(meta.data, function (arc, index) {
_this.updateElement(arc, index, reset);
});
},
updateElement: function updateElement(arc, index, reset) {
var chart = this.chart;
var chartArea = chart.chartArea;
var opts = chart.options;
var animationOpts = opts.animation;
var centerX = (chartArea.left + chartArea.right) / 2;
var centerY = (chartArea.top + chartArea.bottom) / 2;
var startAngle = opts.rotation; // non reset case handled later
var dataset = this.getDataset();
var arcAngle = reset && animationOpts.animateRotate ? 0 : this.calculateArcAngle(dataset.data[index]);
var value = reset && animationOpts.animateScale ? 0 : this.getMetricValue();
var endAngle = startAngle + arcAngle;
var innerRadius = this.innerRadius;
var outerRadius = this.outerRadius;
var valueAtIndexOrDefault = helpers.valueAtIndexOrDefault;
helpers.extend(arc, {
// Utility
_datasetIndex: this.index,
_index: index,
// Desired view properties
_model: {
x: centerX,
y: centerY,
startAngle: startAngle,
endAngle: endAngle,
outerRadius: outerRadius,
innerRadius: innerRadius,
label: valueAtIndexOrDefault(dataset.label, index, chart.data.labels[index]),
roundedCorners: opts.roundedCorners,
value: value
}
});
var model = arc._model;
// Resets the visual styles
var custom = arc.custom || {};
var valueOrDefault = helpers.valueAtIndexOrDefault;
var elementOpts = this.chart.options.elements.arc;
model.backgroundColor = custom.backgroundColor ? custom.backgroundColor : valueOrDefault(dataset.backgroundColor, index, elementOpts.backgroundColor);
model.borderColor = custom.borderColor ? custom.borderColor : valueOrDefault(dataset.borderColor, index, elementOpts.borderColor);
model.borderWidth = custom.borderWidth ? custom.borderWidth : valueOrDefault(dataset.borderWidth, index, elementOpts.borderWidth);
arc.pivot();
},
getMetricValue: function getMetricValue() {
var value = this.getDataset().data[0];
if (value == null) {
value = this.chart.options.domain[0];
}
return value;
},
getDomain: function getDomain() {
return this.chart.options.domain;
},
calculateArcAngle: function calculateArcAngle() {
var _getDomain = this.getDomain(),
_getDomain2 = slicedToArray(_getDomain, 2),
domainStart = _getDomain2[0],
domainEnd = _getDomain2[1];
var value = this.getMetricValue();
var domainSize = domainEnd - domainStart;
return domainSize > 0 ? Math.PI * 2.0 * (Math.abs(value - domainStart) / domainSize) : 0;
},
// gets the max border or hover width to properly scale pie charts
getMaxBorderWidth: function getMaxBorderWidth(arcs) {
var max = 0;
var index = this.index;
var length = arcs.length;
var borderWidth = void 0;
var hoverWidth = void 0;
for (var i = 0; i < length; i++) {
borderWidth = arcs[i]._model ? arcs[i]._model.borderWidth : 0;
hoverWidth = arcs[i]._chart ? arcs[i]._chart.config.data.datasets[index].hoverBorderWidth : 0;
max = borderWidth > max ? borderWidth : max;
max = hoverWidth > max ? hoverWidth : max;
}
return max;
}
});
});
var RadialGaugeChart = (function (Chart$$1) {
Chart$$1.RadialGauge = function (context, config) {
config.type = 'radialGauge';
return new Chart$$1(context, config);
};
});
RadialGaugeController(Chart__default);
RadialGaugeChart(Chart__default);
return RadialGaugeChart;
})));