/**
- * Path the arc, respecting the border radius
- *
- * 8 points of interest exist around the arc segment.
- * These points define the intersection of the arc edges and the corners.
+ * Path the arc, respecting border radius by separating into left and right halves.
*
* Start End
*
- * 1---------2 Outer
+ * 1--->a--->2 Outer
* / \
* 8 3
* | |
* | |
* 7 4
* \ /
- * 6---------5 Inner
+ * 6<---b<---5 Inner
*/
function pathArc(
ctx: CanvasRenderingContext2D,
ctx.beginPath();
if (circular) {
- // The first arc segment from point 1 to point 2
- ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle);
+ // The first arc segments from point 1 to point a to point 2
+ const outerMidAdjustedAngle = (outerStartAdjustedAngle + outerEndAdjustedAngle) / 2;
+ ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerMidAdjustedAngle);
+ ctx.arc(x, y, outerRadius, outerMidAdjustedAngle, outerEndAdjustedAngle);
// The corner segment from point 2 to point 3
if (outerEnd > 0) {
ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);
}
- // The inner arc from point 5 to point 6
- ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), startAngle + (innerStart / innerRadius), true);
+ // The inner arc from point 5 to point b to point 6
+ const innerMidAdjustedAngle = ((endAngle - (innerEnd / innerRadius)) + (startAngle + (innerStart / innerRadius))) / 2;
+ ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), innerMidAdjustedAngle, true);
+ ctx.arc(x, y, innerRadius, innerMidAdjustedAngle, startAngle + (innerStart / innerRadius), true);
// The corner segment from point 6 to point 7
if (innerStart > 0) {
const {fullCircles, startAngle, circumference} = element;
let endAngle = element.endAngle;
if (fullCircles) {
- pathArc(ctx, element, offset, spacing, startAngle + TAU, circular);
-
+ pathArc(ctx, element, offset, spacing, endAngle, circular);
for (let i = 0; i < fullCircles; ++i) {
ctx.fill();
}
-
if (!isNaN(circumference)) {
- endAngle = startAngle + circumference % TAU;
- if (circumference % TAU === 0) {
- endAngle += TAU;
- }
+ endAngle = startAngle + (circumference % TAU || TAU);
}
}
pathArc(ctx, element, offset, spacing, endAngle, circular);
return endAngle;
}
-function drawFullCircleBorders(ctx: CanvasRenderingContext2D, element: ArcElement, inner: boolean) {
- const {x, y, startAngle, pixelMargin, fullCircles} = element;
- const outerRadius = Math.max(element.outerRadius - pixelMargin, 0);
- const innerRadius = element.innerRadius + pixelMargin;
-
- let i;
-
- if (inner) {
- clipArc(ctx, element, startAngle + TAU);
- }
-
- ctx.beginPath();
- ctx.arc(x, y, innerRadius, startAngle + TAU, startAngle, true);
- for (i = 0; i < fullCircles; ++i) {
- ctx.stroke();
- }
-
- ctx.beginPath();
- ctx.arc(x, y, outerRadius, startAngle, startAngle + TAU);
- for (i = 0; i < fullCircles; ++i) {
- ctx.stroke();
- }
-}
-
function drawBorder(
ctx: CanvasRenderingContext2D,
element: ArcElement,
offset: number,
spacing: number,
- endAngle: number,
circular: boolean,
) {
- const {options} = element;
+ const {fullCircles, startAngle, circumference, options} = element;
const {borderWidth, borderJoinStyle} = options;
const inner = options.borderAlign === 'inner';
ctx.lineJoin = borderJoinStyle || 'bevel';
}
- if (element.fullCircles) {
- drawFullCircleBorders(ctx, element, inner);
+ let endAngle = element.endAngle;
+ if (fullCircles) {
+ pathArc(ctx, element, offset, spacing, endAngle, circular);
+ for (let i = 0; i < fullCircles; ++i) {
+ ctx.stroke();
+ }
+ if (!isNaN(circumference)) {
+ endAngle = startAngle + (circumference % TAU || TAU);
+ }
}
if (inner) {
clipArc(ctx, element, endAngle);
}
- pathArc(ctx, element, offset, spacing, endAngle, circular);
- ctx.stroke();
+ if (!fullCircles) {
+ pathArc(ctx, element, offset, spacing, endAngle, circular);
+ ctx.stroke();
+ }
}
export interface ArcProps extends Point {
ctx.fillStyle = options.backgroundColor;
ctx.strokeStyle = options.borderColor;
- const endAngle = drawArc(ctx, this, radiusOffset, spacing, circular);
- drawBorder(ctx, this, radiusOffset, spacing, endAngle, circular);
+ drawArc(ctx, this, radiusOffset, spacing, circular);
+ drawBorder(ctx, this, radiusOffset, spacing, circular);
ctx.restore();
}