Expose radial scale point label positions (#8588)

This commit is contained in:
Evert Timberg 2021-03-07 16:58:45 -05:00 committed by GitHub
parent 5a48604df7
commit 275fdaf3da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 71 additions and 20 deletions

View File

@ -449,6 +449,7 @@ The private APIs listed below were renamed:
* `DatasetController.resyncElements` was renamed to `DatasetController._resyncElements`
* `LayoutItem.isFullWidth` was renamed to `LayoutItem.isFullSize`
* `RadialLinearScale.setReductions` was renamed to `RadialLinearScale._setReductions`
* `RadialLinearScale.pointLabels` was renamed to `RadialLinearScale._pointLabels`
* `Scale.handleMargins` was renamed to `Scale._handleMargins`
### Changed

View File

@ -90,7 +90,7 @@ function fitWithPointLabels(scale) {
const furthestAngles = {};
let i, textSize, pointPosition;
scale._pointLabelSizes = [];
const labelSizes = [];
const valueCount = scale.chart.data.labels.length;
for (i = 0; i < valueCount; i++) {
@ -98,8 +98,8 @@ function fitWithPointLabels(scale) {
const opts = scale.options.pointLabels.setContext(scale.getContext(i));
const plFont = toFont(opts.font);
scale.ctx.font = plFont.string;
textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale.pointLabels[i]);
scale._pointLabelSizes[i] = textSize;
textSize = measureLabelSize(scale.ctx, plFont.lineHeight, scale._pointLabels[i]);
labelSizes[i] = textSize;
// Add quarter circle to make degree 0 mean top of circle
const angleRadians = scale.getIndexAngle(i);
@ -129,6 +129,51 @@ function fitWithPointLabels(scale) {
}
scale._setReductions(scale.drawingArea, furthestLimits, furthestAngles);
scale._pointLabelItems = [];
// Now that text size is determined, compute the full positions
const opts = scale.options;
const tickBackdropHeight = getTickBackdropHeight(opts);
const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
for (i = 0; i < valueCount; i++) {
// Extra pixels out for some label spacing
const extra = (i === 0 ? tickBackdropHeight / 2 : 0);
const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);
const angle = toDegrees(scale.getIndexAngle(i));
const size = labelSizes[i];
adjustPointPositionForLabelHeight(angle, size, pointLabelPosition);
const textAlign = getTextAlignForAngle(angle);
let left;
if (textAlign === 'left') {
left = pointLabelPosition.x;
} else if (textAlign === 'center') {
left = pointLabelPosition.x - (size.w / 2);
} else {
left = pointLabelPosition.x - size.w;
}
const right = left + size.w;
scale._pointLabelItems[i] = {
// Text position
x: pointLabelPosition.x,
y: pointLabelPosition.y,
// Text rendering data
textAlign,
// Bounding box
left,
top: pointLabelPosition.y,
right,
bottom: pointLabelPosition.y + size.h,
};
}
}
function getTextAlignForAngle(angle) {
@ -153,31 +198,24 @@ function drawPointLabels(scale) {
const ctx = scale.ctx;
const opts = scale.options;
const pointLabelOpts = opts.pointLabels;
const tickBackdropHeight = getTickBackdropHeight(opts);
const outerDistance = scale.getDistanceFromCenterForValue(opts.ticks.reverse ? scale.min : scale.max);
ctx.save();
ctx.textBaseline = 'middle';
for (let i = scale.chart.data.labels.length - 1; i >= 0; i--) {
// Extra pixels out for some label spacing
const extra = (i === 0 ? tickBackdropHeight / 2 : 0);
const pointLabelPosition = scale.getPointPosition(i, outerDistance + extra + 5);
const optsAtIndex = pointLabelOpts.setContext(scale.getContext(i));
const plFont = toFont(optsAtIndex.font);
const angle = toDegrees(scale.getIndexAngle(i));
adjustPointPositionForLabelHeight(angle, scale._pointLabelSizes[i], pointLabelPosition);
const {x, y, textAlign} = scale._pointLabelItems[i];
renderText(
ctx,
scale.pointLabels[i],
pointLabelPosition.x,
pointLabelPosition.y + (plFont.lineHeight / 2),
scale._pointLabels[i],
x,
y + (plFont.lineHeight / 2),
plFont,
{
color: optsAtIndex.color,
textAlign: getTextAlignForAngle(angle),
textAlign: textAlign,
}
);
}
@ -238,7 +276,8 @@ export default class RadialLinearScale extends LinearScaleBase {
/** @type {number} */
this.drawingArea = undefined;
/** @type {string[]} */
this.pointLabels = [];
this._pointLabels = [];
this._pointLabelItems = [];
}
setDimensions() {
@ -278,7 +317,7 @@ export default class RadialLinearScale extends LinearScaleBase {
LinearScaleBase.prototype.generateTickLabels.call(me, ticks);
// Point labels
me.pointLabels = me.chart.data.labels.map((value, index) => {
me._pointLabels = me.chart.data.labels.map((value, index) => {
const label = callCallback(me.options.pointLabels.callback, [value, index], me);
return label || label === 0 ? label : '';
});
@ -380,6 +419,16 @@ export default class RadialLinearScale extends LinearScaleBase {
return this.getPointPositionForValue(index || 0, this.getBaseValue());
}
getPointLabelPosition(index) {
const {left, top, right, bottom} = this._pointLabelItems[index];
return {
left,
top,
right,
bottom,
};
}
/**
* @protected
*/

View File

@ -324,7 +324,7 @@ describe('Test the radial linear scale', function() {
});
expect(getLabels(chart.scales.r)).toEqual(['0', '1', '2', '3', '4', '5', '6', '7', '8']);
expect(chart.scales.r.pointLabels).toEqual(['label1', 'label2', 'label3', 'label4', 'label5']);
expect(chart.scales.r._pointLabels).toEqual(['label1', 'label2', 'label3', 'label4', 'label5']);
});
it('Should build point labels using the user supplied callback', function() {
@ -349,7 +349,7 @@ describe('Test the radial linear scale', function() {
}
});
expect(chart.scales.r.pointLabels).toEqual(['0', '1', '2', '3', '4']);
expect(chart.scales.r._pointLabels).toEqual(['0', '1', '2', '3', '4']);
});
it('Should build point labels from falsy values', function() {
@ -363,7 +363,7 @@ describe('Test the radial linear scale', function() {
}
});
expect(chart.scales.r.pointLabels).toEqual([0, '', '', '', '', '']);
expect(chart.scales.r._pointLabels).toEqual([0, '', '', '', '', '']);
});
it('should correctly set the center point', function() {

View File

@ -3089,6 +3089,7 @@ export interface RadialLinearScale<O extends RadialLinearScaleOptions = RadialLi
getValueForDistanceFromCenter(distance: number): number;
getPointPosition(index: number, distanceFromCenter: number): { x: number; y: number; angle: number };
getPointPositionForValue(index: number, value: number): { x: number; y: number; angle: number };
getPointLabelPosition(index: number): ChartArea;
getBasePosition(index: number): { x: number; y: number; angle: number };
}
export const RadialLinearScale: ChartComponent & {