mirror of
https://github.com/chartjs/Chart.js.git
synced 2025-12-08 20:36:08 +00:00
* enable esnext and fix all lint errors * Review update * Missed some * Some cleanup still * Remove leftover eslint disable
192 lines
4.8 KiB
JavaScript
192 lines
4.8 KiB
JavaScript
import defaults from '../core/core.defaults';
|
|
import Element from '../core/core.element';
|
|
import {extend} from '../helpers/helpers.core';
|
|
import {_angleBetween, getAngleFromPoint} from '../helpers/helpers.math';
|
|
const TAU = Math.PI * 2;
|
|
|
|
defaults.set('elements', {
|
|
arc: {
|
|
backgroundColor: defaults.color,
|
|
borderColor: '#fff',
|
|
borderWidth: 2,
|
|
borderAlign: 'center'
|
|
}
|
|
});
|
|
|
|
function clipArc(ctx, arc) {
|
|
const {startAngle, endAngle, pixelMargin, x, y} = arc;
|
|
let angleMargin = pixelMargin / arc.outerRadius;
|
|
|
|
// Draw an inner border by cliping the arc and drawing a double-width border
|
|
// Enlarge the clipping arc by 0.33 pixels to eliminate glitches between borders
|
|
ctx.beginPath();
|
|
ctx.arc(x, y, arc.outerRadius, startAngle - angleMargin, endAngle + angleMargin);
|
|
if (arc.innerRadius > pixelMargin) {
|
|
angleMargin = pixelMargin / arc.innerRadius;
|
|
ctx.arc(x, y, arc.innerRadius - pixelMargin, endAngle + angleMargin, startAngle - angleMargin, true);
|
|
} else {
|
|
ctx.arc(x, y, pixelMargin, endAngle + Math.PI / 2, startAngle - Math.PI / 2);
|
|
}
|
|
ctx.closePath();
|
|
ctx.clip();
|
|
}
|
|
|
|
function drawFullCircleBorders(ctx, vm, arc, inner) {
|
|
const endAngle = arc.endAngle;
|
|
let i;
|
|
|
|
if (inner) {
|
|
arc.endAngle = arc.startAngle + TAU;
|
|
clipArc(ctx, arc);
|
|
arc.endAngle = endAngle;
|
|
if (arc.endAngle === arc.startAngle && arc.fullCircles) {
|
|
arc.endAngle += TAU;
|
|
arc.fullCircles--;
|
|
}
|
|
}
|
|
|
|
ctx.beginPath();
|
|
ctx.arc(arc.x, arc.y, arc.innerRadius, arc.startAngle + TAU, arc.startAngle, true);
|
|
for (i = 0; i < arc.fullCircles; ++i) {
|
|
ctx.stroke();
|
|
}
|
|
|
|
ctx.beginPath();
|
|
ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.startAngle + TAU);
|
|
for (i = 0; i < arc.fullCircles; ++i) {
|
|
ctx.stroke();
|
|
}
|
|
}
|
|
|
|
function drawBorder(ctx, vm, arc) {
|
|
const options = vm.options;
|
|
const inner = options.borderAlign === 'inner';
|
|
|
|
if (inner) {
|
|
ctx.lineWidth = options.borderWidth * 2;
|
|
ctx.lineJoin = 'round';
|
|
} else {
|
|
ctx.lineWidth = options.borderWidth;
|
|
ctx.lineJoin = 'bevel';
|
|
}
|
|
|
|
if (arc.fullCircles) {
|
|
drawFullCircleBorders(ctx, vm, arc, inner);
|
|
}
|
|
|
|
if (inner) {
|
|
clipArc(ctx, arc);
|
|
}
|
|
|
|
ctx.beginPath();
|
|
ctx.arc(arc.x, arc.y, vm.outerRadius, arc.startAngle, arc.endAngle);
|
|
ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
|
|
ctx.closePath();
|
|
ctx.stroke();
|
|
}
|
|
|
|
class Arc extends Element {
|
|
|
|
constructor(cfg) {
|
|
super();
|
|
|
|
this.options = undefined;
|
|
this.circumference = undefined;
|
|
this.startAngle = undefined;
|
|
this.endAngle = undefined;
|
|
this.innerRadius = undefined;
|
|
this.outerRadius = undefined;
|
|
|
|
if (cfg) {
|
|
extend(this, cfg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param {number} chartX
|
|
* @param {number} chartY
|
|
*/
|
|
inRange(chartX, chartY) {
|
|
const me = this;
|
|
|
|
const {angle, distance} = getAngleFromPoint(me, {x: chartX, y: chartY});
|
|
|
|
// Check if within the range of the open/close angle
|
|
const betweenAngles = _angleBetween(angle, me.startAngle, me.endAngle);
|
|
const withinRadius = (distance >= me.innerRadius && distance <= me.outerRadius);
|
|
|
|
return (betweenAngles && withinRadius);
|
|
}
|
|
|
|
getCenterPoint() {
|
|
const me = this;
|
|
const halfAngle = (me.startAngle + me.endAngle) / 2;
|
|
const halfRadius = (me.innerRadius + me.outerRadius) / 2;
|
|
return {
|
|
x: me.x + Math.cos(halfAngle) * halfRadius,
|
|
y: me.y + Math.sin(halfAngle) * halfRadius
|
|
};
|
|
}
|
|
|
|
tooltipPosition() {
|
|
const me = this;
|
|
const centreAngle = me.startAngle + ((me.endAngle - me.startAngle) / 2);
|
|
const rangeFromCentre = (me.outerRadius - me.innerRadius) / 2 + me.innerRadius;
|
|
|
|
return {
|
|
x: me.x + (Math.cos(centreAngle) * rangeFromCentre),
|
|
y: me.y + (Math.sin(centreAngle) * rangeFromCentre)
|
|
};
|
|
}
|
|
|
|
draw(ctx) {
|
|
const me = this;
|
|
const options = me.options;
|
|
const pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0;
|
|
const arc = {
|
|
x: me.x,
|
|
y: me.y,
|
|
innerRadius: me.innerRadius,
|
|
outerRadius: Math.max(me.outerRadius - pixelMargin, 0),
|
|
pixelMargin,
|
|
startAngle: me.startAngle,
|
|
endAngle: me.endAngle,
|
|
fullCircles: Math.floor(me.circumference / TAU)
|
|
};
|
|
let i;
|
|
|
|
ctx.save();
|
|
|
|
ctx.fillStyle = options.backgroundColor;
|
|
ctx.strokeStyle = options.borderColor;
|
|
|
|
if (arc.fullCircles) {
|
|
arc.endAngle = arc.startAngle + TAU;
|
|
ctx.beginPath();
|
|
ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle);
|
|
ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
|
|
ctx.closePath();
|
|
for (i = 0; i < arc.fullCircles; ++i) {
|
|
ctx.fill();
|
|
}
|
|
arc.endAngle = arc.startAngle + me.circumference % TAU;
|
|
}
|
|
|
|
ctx.beginPath();
|
|
ctx.arc(arc.x, arc.y, arc.outerRadius, arc.startAngle, arc.endAngle);
|
|
ctx.arc(arc.x, arc.y, arc.innerRadius, arc.endAngle, arc.startAngle, true);
|
|
ctx.closePath();
|
|
ctx.fill();
|
|
|
|
if (options.borderWidth) {
|
|
drawBorder(ctx, me, arc);
|
|
}
|
|
|
|
ctx.restore();
|
|
}
|
|
}
|
|
|
|
Arc.prototype._type = 'arc';
|
|
|
|
export default Arc;
|