|
1 | 1 | import Element from '../core/core.element.js';
|
2 | 2 | import {_angleBetween, getAngleFromPoint, TAU, HALF_PI, valueOrDefault} from '../helpers/index.js';
|
3 |
| -import {PI, _isBetween, _limitValue} from '../helpers/helpers.math.js'; |
| 3 | +import {PI, _angleDiff, _normalizeAngle, _isBetween, _limitValue} from '../helpers/helpers.math.js'; |
4 | 4 | import {_readValueToProps} from '../helpers/helpers.options.js';
|
5 | 5 | import type {ArcOptions, Point} from '../types/index.js';
|
6 | 6 |
|
| 7 | +function clipSelf(ctx: CanvasRenderingContext2D, element: ArcElement, endAngle: number) { |
| 8 | + const {startAngle, x, y, outerRadius, innerRadius, options} = element; |
| 9 | + const {borderWidth, borderJoinStyle} = options; |
| 10 | + const outerAngleClip = Math.min(borderWidth / outerRadius, _normalizeAngle(startAngle - endAngle)); |
| 11 | + ctx.beginPath(); |
| 12 | + ctx.arc(x, y, outerRadius - borderWidth / 2, startAngle + outerAngleClip / 2, endAngle - outerAngleClip / 2); |
| 13 | + |
| 14 | + if (innerRadius > 0) { |
| 15 | + const innerAngleClip = Math.min(borderWidth / innerRadius, _normalizeAngle(startAngle - endAngle)); |
| 16 | + ctx.arc(x, y, innerRadius + borderWidth / 2, endAngle - innerAngleClip / 2, startAngle + innerAngleClip / 2, true); |
| 17 | + } else { |
| 18 | + const clipWidth = Math.min(borderWidth / 2, outerRadius * _normalizeAngle(startAngle - endAngle)); |
| 19 | + |
| 20 | + if (borderJoinStyle === 'round') { |
| 21 | + ctx.arc(x, y, clipWidth, endAngle - PI / 2, startAngle + PI / 2, true); |
| 22 | + } else if (borderJoinStyle === 'bevel') { |
| 23 | + const r = 2 * clipWidth * clipWidth; |
| 24 | + const endX = -r * Math.cos(endAngle + PI / 2) + x; |
| 25 | + const endY = -r * Math.sin(endAngle + PI / 2) + y; |
| 26 | + const startX = r * Math.cos(startAngle + PI / 2) + x; |
| 27 | + const startY = r * Math.sin(startAngle + PI / 2) + y; |
| 28 | + ctx.lineTo(endX, endY); |
| 29 | + ctx.lineTo(startX, startY); |
| 30 | + } |
| 31 | + } |
| 32 | + ctx.closePath(); |
| 33 | + |
| 34 | + ctx.moveTo(0, 0); |
| 35 | + ctx.rect(0, 0, ctx.canvas.width, ctx.canvas.height); |
| 36 | + |
| 37 | + ctx.clip('evenodd'); |
| 38 | +} |
| 39 | + |
7 | 40 |
|
8 | 41 | function clipArc(ctx: CanvasRenderingContext2D, element: ArcElement, endAngle: number) {
|
9 | 42 | const {startAngle, pixelMargin, x, y, outerRadius, innerRadius} = element;
|
@@ -213,7 +246,7 @@ function drawBorder(
|
213 | 246 | circular: boolean,
|
214 | 247 | ) {
|
215 | 248 | const {fullCircles, startAngle, circumference, options} = element;
|
216 |
| - const {borderWidth, borderJoinStyle, borderDash, borderDashOffset} = options; |
| 249 | + const {borderWidth, borderJoinStyle, borderDash, borderDashOffset, borderRadius} = options; |
217 | 250 | const inner = options.borderAlign === 'inner';
|
218 | 251 |
|
219 | 252 | if (!borderWidth) {
|
@@ -246,6 +279,10 @@ function drawBorder(
|
246 | 279 | clipArc(ctx, element, endAngle);
|
247 | 280 | }
|
248 | 281 |
|
| 282 | + if (options.selfJoin && endAngle - startAngle >= PI && borderRadius === 0 && borderJoinStyle !== 'miter') { |
| 283 | + clipSelf(ctx, element, endAngle); |
| 284 | + } |
| 285 | + |
249 | 286 | if (!fullCircles) {
|
250 | 287 | pathArc(ctx, element, offset, spacing, endAngle, circular);
|
251 | 288 | ctx.stroke();
|
@@ -276,6 +313,7 @@ export default class ArcElement extends Element<ArcProps, ArcOptions> {
|
276 | 313 | spacing: 0,
|
277 | 314 | angle: undefined,
|
278 | 315 | circular: true,
|
| 316 | + selfJoin: false, |
279 | 317 | };
|
280 | 318 |
|
281 | 319 | static defaultRoutes = {
|
|
0 commit comments