mirror of
https://github.com/chartjs/Chart.js.git
synced 2026-03-06 16:26:52 +01:00
Add circular prop to arc element (#10405)
* feat: add circular prop to arc element draw actions * test: add test for arc element with circular:false prop * feat: add circular prop to Arc element options * docs: add decriptiption for new Polar area chart prop * docs: fix circular prop description * docs: add info about arc element circular prop to elements docs * docs: move circular prop from general options to styling
This commit is contained in:
@@ -66,6 +66,7 @@ The following options can be included in a polar area chart dataset to configure
|
||||
| [`hoverBorderColor`](#interactions) | [`Color`](../general/colors.md) | Yes | Yes | `undefined`
|
||||
| [`hoverBorderJoinStyle`](#interactions) | `'round'`\|`'bevel'`\|`'miter'` | Yes | Yes | `undefined`
|
||||
| [`hoverBorderWidth`](#interactions) | `number` | Yes | Yes | `undefined`
|
||||
| [`circular`](#styling) | `boolean` | Yes | Yes | `true`
|
||||
|
||||
All these values, if `undefined`, fallback to the scopes described in [option resolution](../general/options)
|
||||
|
||||
@@ -85,6 +86,7 @@ The style of each arc can be controlled with the following properties:
|
||||
| `borderColor` | arc border color.
|
||||
| `borderJoinStyle` | arc border join style. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin).
|
||||
| `borderWidth` | arc border width (in pixels).
|
||||
| `circular` | By default the Arc is curved. If `circular: false` the Arc will be flat.
|
||||
|
||||
All these values, if `undefined`, fallback to the associated [`elements.arc.*`](../configuration/elements.md#arc-configuration) options.
|
||||
|
||||
|
||||
@@ -101,3 +101,4 @@ Namespace: `options.elements.arc`, global arc options: `Chart.defaults.elements.
|
||||
| `borderColor` | [`Color`](/general/colors.md) | `'#fff'` | Arc stroke color.
|
||||
| `borderJoinStyle` | `'round'`\|`'bevel'`\|`'miter'` | `'bevel'`\|`'round'` | Line join style. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineJoin). The default is `'round'` when `borderAlign` is `'inner'`
|
||||
| `borderWidth`| `number` | `2` | Arc stroke width.
|
||||
| `circular` | `boolean` | `true` | By default the Arc is curved. If `circular: false` the Arc will be flat
|
||||
|
||||
@@ -93,7 +93,7 @@ function rThetaToXY(r, theta, x, y) {
|
||||
* @param {CanvasRenderingContext2D} ctx
|
||||
* @param {ArcElement} element
|
||||
*/
|
||||
function pathArc(ctx, element, offset, spacing, end) {
|
||||
function pathArc(ctx, element, offset, spacing, end, circular) {
|
||||
const {x, y, startAngle: start, pixelMargin, innerRadius: innerR} = element;
|
||||
|
||||
const outerRadius = Math.max(element.outerRadius + spacing + offset - pixelMargin, 0);
|
||||
@@ -131,52 +131,64 @@ function pathArc(ctx, element, offset, spacing, end) {
|
||||
|
||||
ctx.beginPath();
|
||||
|
||||
// The first arc segment from point 1 to point 2
|
||||
ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle);
|
||||
if (circular) {
|
||||
// The first arc segment from point 1 to point 2
|
||||
ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle);
|
||||
|
||||
// The corner segment from point 2 to point 3
|
||||
if (outerEnd > 0) {
|
||||
const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y);
|
||||
ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI);
|
||||
}
|
||||
// The corner segment from point 2 to point 3
|
||||
if (outerEnd > 0) {
|
||||
const pCenter = rThetaToXY(outerEndAdjustedRadius, outerEndAdjustedAngle, x, y);
|
||||
ctx.arc(pCenter.x, pCenter.y, outerEnd, outerEndAdjustedAngle, endAngle + HALF_PI);
|
||||
}
|
||||
|
||||
// The line from point 3 to point 4
|
||||
const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y);
|
||||
ctx.lineTo(p4.x, p4.y);
|
||||
// The line from point 3 to point 4
|
||||
const p4 = rThetaToXY(innerEndAdjustedRadius, endAngle, x, y);
|
||||
ctx.lineTo(p4.x, p4.y);
|
||||
|
||||
// The corner segment from point 4 to point 5
|
||||
if (innerEnd > 0) {
|
||||
const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y);
|
||||
ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);
|
||||
}
|
||||
// The corner segment from point 4 to point 5
|
||||
if (innerEnd > 0) {
|
||||
const pCenter = rThetaToXY(innerEndAdjustedRadius, innerEndAdjustedAngle, x, y);
|
||||
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 6
|
||||
ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), startAngle + (innerStart / innerRadius), true);
|
||||
|
||||
// The corner segment from point 6 to point 7
|
||||
if (innerStart > 0) {
|
||||
const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y);
|
||||
ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI);
|
||||
}
|
||||
// The corner segment from point 6 to point 7
|
||||
if (innerStart > 0) {
|
||||
const pCenter = rThetaToXY(innerStartAdjustedRadius, innerStartAdjustedAngle, x, y);
|
||||
ctx.arc(pCenter.x, pCenter.y, innerStart, innerStartAdjustedAngle + Math.PI, startAngle - HALF_PI);
|
||||
}
|
||||
|
||||
// The line from point 7 to point 8
|
||||
const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y);
|
||||
ctx.lineTo(p8.x, p8.y);
|
||||
// The line from point 7 to point 8
|
||||
const p8 = rThetaToXY(outerStartAdjustedRadius, startAngle, x, y);
|
||||
ctx.lineTo(p8.x, p8.y);
|
||||
|
||||
// The corner segment from point 8 to point 1
|
||||
if (outerStart > 0) {
|
||||
const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y);
|
||||
ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle);
|
||||
// The corner segment from point 8 to point 1
|
||||
if (outerStart > 0) {
|
||||
const pCenter = rThetaToXY(outerStartAdjustedRadius, outerStartAdjustedAngle, x, y);
|
||||
ctx.arc(pCenter.x, pCenter.y, outerStart, startAngle - HALF_PI, outerStartAdjustedAngle);
|
||||
}
|
||||
} else {
|
||||
ctx.moveTo(x, y);
|
||||
|
||||
const outerStartX = Math.cos(outerStartAdjustedAngle) * outerRadius + x;
|
||||
const outerStartY = Math.sin(outerStartAdjustedAngle) * outerRadius + y;
|
||||
ctx.lineTo(outerStartX, outerStartY);
|
||||
|
||||
const outerEndX = Math.cos(outerEndAdjustedAngle) * outerRadius + x;
|
||||
const outerEndY = Math.sin(outerEndAdjustedAngle) * outerRadius + y;
|
||||
ctx.lineTo(outerEndX, outerEndY);
|
||||
}
|
||||
|
||||
ctx.closePath();
|
||||
}
|
||||
|
||||
function drawArc(ctx, element, offset, spacing) {
|
||||
function drawArc(ctx, element, offset, spacing, circular) {
|
||||
const {fullCircles, startAngle, circumference} = element;
|
||||
let endAngle = element.endAngle;
|
||||
if (fullCircles) {
|
||||
pathArc(ctx, element, offset, spacing, startAngle + TAU);
|
||||
pathArc(ctx, element, offset, spacing, startAngle + TAU, circular);
|
||||
|
||||
for (let i = 0; i < fullCircles; ++i) {
|
||||
ctx.fill();
|
||||
@@ -189,8 +201,7 @@ function drawArc(ctx, element, offset, spacing) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pathArc(ctx, element, offset, spacing, endAngle);
|
||||
pathArc(ctx, element, offset, spacing, endAngle, circular);
|
||||
ctx.fill();
|
||||
return endAngle;
|
||||
}
|
||||
@@ -219,7 +230,7 @@ function drawFullCircleBorders(ctx, element, inner) {
|
||||
}
|
||||
}
|
||||
|
||||
function drawBorder(ctx, element, offset, spacing, endAngle) {
|
||||
function drawBorder(ctx, element, offset, spacing, endAngle, circular) {
|
||||
const {options} = element;
|
||||
const {borderWidth, borderJoinStyle} = options;
|
||||
const inner = options.borderAlign === 'inner';
|
||||
@@ -244,7 +255,7 @@ function drawBorder(ctx, element, offset, spacing, endAngle) {
|
||||
clipArc(ctx, element, endAngle);
|
||||
}
|
||||
|
||||
pathArc(ctx, element, offset, spacing, endAngle);
|
||||
pathArc(ctx, element, offset, spacing, endAngle, circular);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
@@ -323,6 +334,7 @@ export default class ArcElement extends Element {
|
||||
const {options, circumference} = this;
|
||||
const offset = (options.offset || 0) / 2;
|
||||
const spacing = (options.spacing || 0) / 2;
|
||||
const circular = options.circular;
|
||||
this.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0;
|
||||
this.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;
|
||||
|
||||
@@ -345,8 +357,8 @@ export default class ArcElement extends Element {
|
||||
ctx.fillStyle = options.backgroundColor;
|
||||
ctx.strokeStyle = options.borderColor;
|
||||
|
||||
const endAngle = drawArc(ctx, this, radiusOffset, spacing);
|
||||
drawBorder(ctx, this, radiusOffset, spacing, endAngle);
|
||||
const endAngle = drawArc(ctx, this, radiusOffset, spacing, circular);
|
||||
drawBorder(ctx, this, radiusOffset, spacing, endAngle, circular);
|
||||
|
||||
ctx.restore();
|
||||
}
|
||||
@@ -366,6 +378,7 @@ ArcElement.defaults = {
|
||||
offset: 0,
|
||||
spacing: 0,
|
||||
angle: undefined,
|
||||
circular: true,
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -218,4 +218,36 @@ describe('Arc element tests', function() {
|
||||
|
||||
expect(ctx.getCalls().length).toBe(0);
|
||||
});
|
||||
|
||||
it('should draw when circular: false', function() {
|
||||
var arc = new Chart.elements.ArcElement({
|
||||
startAngle: 0,
|
||||
endAngle: Math.PI * 2,
|
||||
x: 2,
|
||||
y: 2,
|
||||
innerRadius: 0,
|
||||
outerRadius: 2,
|
||||
options: {
|
||||
spacing: 0,
|
||||
offset: 0,
|
||||
scales: {
|
||||
r: {
|
||||
grid: {
|
||||
circular: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
elements: {
|
||||
arc: {
|
||||
circular: false
|
||||
},
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
var ctx = window.createMockContext();
|
||||
arc.draw(ctx);
|
||||
|
||||
expect(ctx.getCalls().length).toBeGreaterThan(0);
|
||||
});
|
||||
});
|
||||
|
||||
6
types/index.esm.d.ts
vendored
6
types/index.esm.d.ts
vendored
@@ -1746,6 +1746,12 @@ export interface ArcOptions extends CommonElementOptions {
|
||||
* Arc offset (in pixels).
|
||||
*/
|
||||
offset: number;
|
||||
|
||||
/**
|
||||
* If false, Arc will be flat.
|
||||
* @default true
|
||||
*/
|
||||
circular: boolean;
|
||||
}
|
||||
|
||||
export interface ArcHoverOptions extends CommonHoverOptions {
|
||||
|
||||
Reference in New Issue
Block a user