mirror of
https://github.com/chartjs/Chart.js.git
synced 2024-10-06 04:09:08 +02:00
fix: Always draw full arcs and borders for doughnut slices (#10806)
* test: Add a failing test for single-slice doughnut with offset * fix: Always draw full arcs and borders for doughnut slices Fixes #10801 * test: Update existing image
This commit is contained in:
parent
69175847ef
commit
89487501b6
@ -67,21 +67,18 @@ function rThetaToXY(r: number, theta: number, x: number, y: number) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path the arc, respecting the border radius
|
* Path the arc, respecting border radius by separating into left and right halves.
|
||||||
*
|
|
||||||
* 8 points of interest exist around the arc segment.
|
|
||||||
* These points define the intersection of the arc edges and the corners.
|
|
||||||
*
|
*
|
||||||
* Start End
|
* Start End
|
||||||
*
|
*
|
||||||
* 1---------2 Outer
|
* 1--->a--->2 Outer
|
||||||
* / \
|
* / \
|
||||||
* 8 3
|
* 8 3
|
||||||
* | |
|
* | |
|
||||||
* | |
|
* | |
|
||||||
* 7 4
|
* 7 4
|
||||||
* \ /
|
* \ /
|
||||||
* 6---------5 Inner
|
* 6<---b<---5 Inner
|
||||||
*/
|
*/
|
||||||
function pathArc(
|
function pathArc(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
@ -129,8 +126,10 @@ function pathArc(
|
|||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
|
|
||||||
if (circular) {
|
if (circular) {
|
||||||
// The first arc segment from point 1 to point 2
|
// The first arc segments from point 1 to point a to point 2
|
||||||
ctx.arc(x, y, outerRadius, outerStartAdjustedAngle, outerEndAdjustedAngle);
|
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
|
// The corner segment from point 2 to point 3
|
||||||
if (outerEnd > 0) {
|
if (outerEnd > 0) {
|
||||||
@ -148,8 +147,10 @@ function pathArc(
|
|||||||
ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);
|
ctx.arc(pCenter.x, pCenter.y, innerEnd, endAngle + HALF_PI, innerEndAdjustedAngle + Math.PI);
|
||||||
}
|
}
|
||||||
|
|
||||||
// The inner arc from point 5 to point 6
|
// The inner arc from point 5 to point b to point 6
|
||||||
ctx.arc(x, y, innerRadius, endAngle - (innerEnd / innerRadius), startAngle + (innerStart / innerRadius), true);
|
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
|
// The corner segment from point 6 to point 7
|
||||||
if (innerStart > 0) {
|
if (innerStart > 0) {
|
||||||
@ -191,17 +192,12 @@ function drawArc(
|
|||||||
const {fullCircles, startAngle, circumference} = element;
|
const {fullCircles, startAngle, circumference} = element;
|
||||||
let endAngle = element.endAngle;
|
let endAngle = element.endAngle;
|
||||||
if (fullCircles) {
|
if (fullCircles) {
|
||||||
pathArc(ctx, element, offset, spacing, startAngle + TAU, circular);
|
pathArc(ctx, element, offset, spacing, endAngle, circular);
|
||||||
|
|
||||||
for (let i = 0; i < fullCircles; ++i) {
|
for (let i = 0; i < fullCircles; ++i) {
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isNaN(circumference)) {
|
if (!isNaN(circumference)) {
|
||||||
endAngle = startAngle + circumference % TAU;
|
endAngle = startAngle + (circumference % TAU || TAU);
|
||||||
if (circumference % TAU === 0) {
|
|
||||||
endAngle += TAU;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pathArc(ctx, element, offset, spacing, endAngle, circular);
|
pathArc(ctx, element, offset, spacing, endAngle, circular);
|
||||||
@ -209,39 +205,14 @@ function drawArc(
|
|||||||
return endAngle;
|
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(
|
function drawBorder(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
element: ArcElement,
|
element: ArcElement,
|
||||||
offset: number,
|
offset: number,
|
||||||
spacing: number,
|
spacing: number,
|
||||||
endAngle: number,
|
|
||||||
circular: boolean,
|
circular: boolean,
|
||||||
) {
|
) {
|
||||||
const {options} = element;
|
const {fullCircles, startAngle, circumference, options} = element;
|
||||||
const {borderWidth, borderJoinStyle} = options;
|
const {borderWidth, borderJoinStyle} = options;
|
||||||
const inner = options.borderAlign === 'inner';
|
const inner = options.borderAlign === 'inner';
|
||||||
|
|
||||||
@ -257,16 +228,25 @@ function drawBorder(
|
|||||||
ctx.lineJoin = borderJoinStyle || 'bevel';
|
ctx.lineJoin = borderJoinStyle || 'bevel';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (element.fullCircles) {
|
let endAngle = element.endAngle;
|
||||||
drawFullCircleBorders(ctx, element, inner);
|
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) {
|
if (inner) {
|
||||||
clipArc(ctx, element, endAngle);
|
clipArc(ctx, element, endAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!fullCircles) {
|
||||||
pathArc(ctx, element, offset, spacing, endAngle, circular);
|
pathArc(ctx, element, offset, spacing, endAngle, circular);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ArcProps extends Point {
|
export interface ArcProps extends Point {
|
||||||
@ -385,8 +365,8 @@ export default class ArcElement extends Element<ArcProps, ArcOptions> {
|
|||||||
ctx.fillStyle = options.backgroundColor;
|
ctx.fillStyle = options.backgroundColor;
|
||||||
ctx.strokeStyle = options.borderColor;
|
ctx.strokeStyle = options.borderColor;
|
||||||
|
|
||||||
const endAngle = drawArc(ctx, this, radiusOffset, spacing, circular);
|
drawArc(ctx, this, radiusOffset, spacing, circular);
|
||||||
drawBorder(ctx, this, radiusOffset, spacing, endAngle, circular);
|
drawBorder(ctx, this, radiusOffset, spacing, circular);
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 11 KiB |
16
test/fixtures/controller.doughnut/single-slice-offset.js
vendored
Normal file
16
test/fixtures/controller.doughnut/single-slice-offset.js
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
module.exports = {
|
||||||
|
config: {
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
labels: ['A'],
|
||||||
|
datasets: [{
|
||||||
|
data: [385],
|
||||||
|
backgroundColor: 'rgba(0,0,0,0.3)',
|
||||||
|
borderColor: 'rgba(0,0,0,0.5)',
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
offset: 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
BIN
test/fixtures/controller.doughnut/single-slice-offset.png
vendored
Normal file
BIN
test/fixtures/controller.doughnut/single-slice-offset.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
Loading…
Reference in New Issue
Block a user