mirror of
https://github.com/chartjs/Chart.js.git
synced 2024-10-06 12:19:08 +02:00
Stop mutating arc state while drawing (#9153)
* Stop mutating arc state while drawing * No need for default values * Nits * Remove #9152 * Use correct endAngle for clipping
This commit is contained in:
parent
1d047355e7
commit
03eb826f8c
@ -3,8 +3,8 @@ import {_angleBetween, getAngleFromPoint, TAU, HALF_PI} from '../helpers/index';
|
|||||||
import {PI, _limitValue} from '../helpers/helpers.math';
|
import {PI, _limitValue} from '../helpers/helpers.math';
|
||||||
import {_readValueToProps} from '../helpers/helpers.options';
|
import {_readValueToProps} from '../helpers/helpers.options';
|
||||||
|
|
||||||
function clipArc(ctx, element) {
|
function clipArc(ctx, element, endAngle) {
|
||||||
const {startAngle, endAngle, pixelMargin, x, y, outerRadius, innerRadius} = element;
|
const {startAngle, pixelMargin, x, y, outerRadius, innerRadius} = element;
|
||||||
let angleMargin = pixelMargin / outerRadius;
|
let angleMargin = pixelMargin / outerRadius;
|
||||||
|
|
||||||
// Draw an inner border by clipping the arc and drawing a double-width border
|
// Draw an inner border by clipping the arc and drawing a double-width border
|
||||||
@ -93,8 +93,8 @@ function rThetaToXY(r, theta, x, y) {
|
|||||||
* @param {CanvasRenderingContext2D} ctx
|
* @param {CanvasRenderingContext2D} ctx
|
||||||
* @param {ArcElement} element
|
* @param {ArcElement} element
|
||||||
*/
|
*/
|
||||||
function pathArc(ctx, element, offset) {
|
function pathArc(ctx, element, offset, end) {
|
||||||
const {x, y, startAngle: start, endAngle: end, pixelMargin, innerRadius: innerR} = element;
|
const {x, y, startAngle: start, pixelMargin, innerRadius: innerR} = element;
|
||||||
|
|
||||||
const outerRadius = Math.max(element.outerRadius + offset - pixelMargin, 0);
|
const outerRadius = Math.max(element.outerRadius + offset - pixelMargin, 0);
|
||||||
const innerRadius = innerR > 0 ? innerR + offset + pixelMargin : 0;
|
const innerRadius = innerR > 0 ? innerR + offset + pixelMargin : 0;
|
||||||
@ -159,54 +159,53 @@ function pathArc(ctx, element, offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function drawArc(ctx, element, offset) {
|
function drawArc(ctx, element, offset) {
|
||||||
if (element.fullCircles) {
|
const {fullCircles, startAngle, circumference} = element;
|
||||||
element.endAngle = element.startAngle + TAU;
|
let endAngle = element.endAngle;
|
||||||
|
if (fullCircles) {
|
||||||
|
pathArc(ctx, element, offset, startAngle + TAU);
|
||||||
|
|
||||||
pathArc(ctx, element, offset);
|
for (let i = 0; i < fullCircles; ++i) {
|
||||||
|
|
||||||
for (let i = 0; i < element.fullCircles; ++i) {
|
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isNaN(circumference)) {
|
||||||
|
endAngle = startAngle + circumference % TAU;
|
||||||
|
if (circumference % TAU === 0) {
|
||||||
|
endAngle += TAU;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!isNaN(element.circumference)) {
|
|
||||||
element.endAngle = element.startAngle + element.circumference % TAU;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pathArc(ctx, element, offset);
|
pathArc(ctx, element, offset, endAngle);
|
||||||
ctx.fill();
|
ctx.fill();
|
||||||
|
return endAngle;
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawFullCircleBorders(ctx, element, inner) {
|
function drawFullCircleBorders(ctx, element, inner) {
|
||||||
const {x, y, startAngle, endAngle, pixelMargin} = element;
|
const {x, y, startAngle, pixelMargin, fullCircles} = element;
|
||||||
const outerRadius = Math.max(element.outerRadius - pixelMargin, 0);
|
const outerRadius = Math.max(element.outerRadius - pixelMargin, 0);
|
||||||
const innerRadius = element.innerRadius + pixelMargin;
|
const innerRadius = element.innerRadius + pixelMargin;
|
||||||
|
|
||||||
let i;
|
let i;
|
||||||
|
|
||||||
if (inner) {
|
if (inner) {
|
||||||
element.endAngle = element.startAngle + TAU;
|
clipArc(ctx, element, startAngle + TAU);
|
||||||
clipArc(ctx, element);
|
|
||||||
element.endAngle = endAngle;
|
|
||||||
if (element.endAngle === element.startAngle) {
|
|
||||||
element.endAngle += TAU;
|
|
||||||
element.fullCircles--;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(x, y, innerRadius, startAngle + TAU, startAngle, true);
|
ctx.arc(x, y, innerRadius, startAngle + TAU, startAngle, true);
|
||||||
for (i = 0; i < element.fullCircles; ++i) {
|
for (i = 0; i < fullCircles; ++i) {
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.arc(x, y, outerRadius, startAngle, startAngle + TAU);
|
ctx.arc(x, y, outerRadius, startAngle, startAngle + TAU);
|
||||||
for (i = 0; i < element.fullCircles; ++i) {
|
for (i = 0; i < fullCircles; ++i) {
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawBorder(ctx, element, offset) {
|
function drawBorder(ctx, element, offset, endAngle) {
|
||||||
const {options} = element;
|
const {options} = element;
|
||||||
const inner = options.borderAlign === 'inner';
|
const inner = options.borderAlign === 'inner';
|
||||||
|
|
||||||
@ -227,10 +226,10 @@ function drawBorder(ctx, element, offset) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (inner) {
|
if (inner) {
|
||||||
clipArc(ctx, element);
|
clipArc(ctx, element, endAngle);
|
||||||
}
|
}
|
||||||
|
|
||||||
pathArc(ctx, element, offset);
|
pathArc(ctx, element, offset, endAngle);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +277,7 @@ export default class ArcElement extends Element {
|
|||||||
* @param {boolean} [useFinalPosition]
|
* @param {boolean} [useFinalPosition]
|
||||||
*/
|
*/
|
||||||
getCenterPoint(useFinalPosition) {
|
getCenterPoint(useFinalPosition) {
|
||||||
const {x, y, startAngle, endAngle, innerRadius, outerRadius, circumference} = this.getProps([
|
const {x, y, startAngle, endAngle, innerRadius, outerRadius} = this.getProps([
|
||||||
'x',
|
'x',
|
||||||
'y',
|
'y',
|
||||||
'startAngle',
|
'startAngle',
|
||||||
@ -287,7 +286,7 @@ export default class ArcElement extends Element {
|
|||||||
'outerRadius',
|
'outerRadius',
|
||||||
'circumference'
|
'circumference'
|
||||||
], useFinalPosition);
|
], useFinalPosition);
|
||||||
const halfAngle = isNaN(circumference) ? (startAngle + endAngle) / 2 : startAngle + circumference / 2;
|
const halfAngle = (startAngle + endAngle) / 2;
|
||||||
const halfRadius = (innerRadius + outerRadius) / 2;
|
const halfRadius = (innerRadius + outerRadius) / 2;
|
||||||
return {
|
return {
|
||||||
x: x + Math.cos(halfAngle) * halfRadius,
|
x: x + Math.cos(halfAngle) * halfRadius,
|
||||||
@ -304,12 +303,12 @@ export default class ArcElement extends Element {
|
|||||||
|
|
||||||
draw(ctx) {
|
draw(ctx) {
|
||||||
const me = this;
|
const me = this;
|
||||||
const options = me.options;
|
const {options, circumference} = me;
|
||||||
const offset = (options.offset || 0) / 2;
|
const offset = (options.offset || 0) / 2;
|
||||||
me.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0;
|
me.pixelMargin = (options.borderAlign === 'inner') ? 0.33 : 0;
|
||||||
me.fullCircles = Math.floor(me.circumference / TAU);
|
me.fullCircles = circumference > TAU ? Math.floor(circumference / TAU) : 0;
|
||||||
|
|
||||||
if (me.circumference === 0 || me.innerRadius < 0 || me.outerRadius < 0) {
|
if (circumference === 0 || me.innerRadius < 0 || me.outerRadius < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,8 +327,8 @@ export default class ArcElement extends Element {
|
|||||||
ctx.fillStyle = options.backgroundColor;
|
ctx.fillStyle = options.backgroundColor;
|
||||||
ctx.strokeStyle = options.borderColor;
|
ctx.strokeStyle = options.borderColor;
|
||||||
|
|
||||||
drawArc(ctx, me, radiusOffset);
|
const endAngle = drawArc(ctx, me, radiusOffset);
|
||||||
drawBorder(ctx, me, radiusOffset);
|
drawBorder(ctx, me, radiusOffset, endAngle);
|
||||||
|
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
|
@ -66,7 +66,7 @@ describe('Arc element tests', function() {
|
|||||||
expect(center.y).toBeCloseTo(0.5, 6);
|
expect(center.y).toBeCloseTo(0.5, 6);
|
||||||
});
|
});
|
||||||
|
|
||||||
it ('should get the center of full circle using endAngle', function() {
|
it ('should get the center of full circle before and after draw', function() {
|
||||||
// Mock out the arc as if the controller put it there
|
// Mock out the arc as if the controller put it there
|
||||||
var arc = new Chart.elements.ArcElement({
|
var arc = new Chart.elements.ArcElement({
|
||||||
startAngle: 0,
|
startAngle: 0,
|
||||||
@ -74,27 +74,18 @@ describe('Arc element tests', function() {
|
|||||||
x: 2,
|
x: 2,
|
||||||
y: 2,
|
y: 2,
|
||||||
innerRadius: 0,
|
innerRadius: 0,
|
||||||
outerRadius: 2
|
outerRadius: 2,
|
||||||
|
options: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
var center = arc.getCenterPoint();
|
var center = arc.getCenterPoint();
|
||||||
expect(center.x).toBeCloseTo(1, 6);
|
expect(center.x).toBeCloseTo(1, 6);
|
||||||
expect(center.y).toBeCloseTo(2, 6);
|
expect(center.y).toBeCloseTo(2, 6);
|
||||||
});
|
|
||||||
|
|
||||||
it ('should get the center of full circle using circumference', function() {
|
var ctx = window.createMockContext();
|
||||||
// Mock out the arc as if the controller put it there
|
arc.draw(ctx);
|
||||||
var arc = new Chart.elements.ArcElement({
|
|
||||||
startAngle: 0,
|
|
||||||
endAngle: 0,
|
|
||||||
x: 2,
|
|
||||||
y: 2,
|
|
||||||
innerRadius: 0,
|
|
||||||
outerRadius: 2,
|
|
||||||
circumference: Math.PI * 2
|
|
||||||
});
|
|
||||||
|
|
||||||
var center = arc.getCenterPoint();
|
center = arc.getCenterPoint();
|
||||||
expect(center.x).toBeCloseTo(1, 6);
|
expect(center.x).toBeCloseTo(1, 6);
|
||||||
expect(center.y).toBeCloseTo(2, 6);
|
expect(center.y).toBeCloseTo(2, 6);
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user