diff --git a/__tests__/plots/static/index.ts b/__tests__/plots/static/index.ts index cfd9d2620a..c981fe33bf 100644 --- a/__tests__/plots/static/index.ts +++ b/__tests__/plots/static/index.ts @@ -345,3 +345,4 @@ export { mockPieSpiderHide } from './mock-pie-spider-hide'; export { mockPieSpiderExceed } from './mock-pie-spider-exceed'; export { mockFacetPieLegend } from './mock-facet-pie-legend'; export { weatherLineMultiAxesSync } from './weather-line-multi-axes-sync'; +export { mockPieOutside } from './mock-pie-outside'; diff --git a/__tests__/plots/static/mock-pie-outside.ts b/__tests__/plots/static/mock-pie-outside.ts new file mode 100644 index 0000000000..449ac878e2 --- /dev/null +++ b/__tests__/plots/static/mock-pie-outside.ts @@ -0,0 +1,31 @@ +import { G2Spec } from '../../../src'; + +export function mockPieOutside(): G2Spec { + return { + type: 'interval', + data: [ + { type: '微博A', value: 93.33 }, + { type: '其他A', value: 6.67 }, + { type: '论坛A', value: 4.77 }, + { type: '网站A', value: 1.44 }, + { type: '微信A', value: 1.12 }, + { type: '客户A', value: 1.05 }, + { type: '新闻A', value: 0.81 }, + { type: '视频A', value: 0.39 }, + { type: '博客A', value: 0.37 }, + { type: '报刊A', value: 0.17 }, + ], + encode: { + y: 'value', + color: 'type', + }, + labels: [ + { + position: 'outside', + text: (obj) => `${obj.type} (${obj.value})`, + }, + ], + transform: [{ type: 'stackY' }], + coordinate: { type: 'theta' }, + }; +} diff --git a/src/shape/label/position/outside.ts b/src/shape/label/position/outside.ts index 21dc814238..bbfbfcb16e 100644 --- a/src/shape/label/position/outside.ts +++ b/src/shape/label/position/outside.ts @@ -2,6 +2,7 @@ import { Coordinate } from '@antv/coord'; import { Vector2 } from '../../../runtime'; import { getArcObject } from '../../../shape/utils'; import { isCircular, isRadial } from '../../../utils/coordinate'; +import { hideAndDodgeYAndMoveX } from './utils'; import type { LabelPosition } from './default'; import { @@ -32,7 +33,6 @@ export function radiusOf(points, value, coordinate) { export function angleOf(points, value, coordinate) { const arcObject = getArcObject(coordinate, points, [value.y, value.y1]); const { startAngle, endAngle } = arcObject; - return (startAngle + endAngle) / 2; } @@ -93,14 +93,44 @@ export function inferOutsideCircularStyle( y: y2, ...textStyle, ...connectorStyle, + angle, + radius, }; } +const styleByPoints = new WeakMap(); + +export function inferOutsideForPie( + position: LabelPosition, + points: Vector2[], + value: Record, + coordinate: Coordinate, + options: Record, + labels: Vector2[][], +) { + if (!isCircular(coordinate)) return {}; + if (styleByPoints.has(points)) return styleByPoints.get(points); + const computed = labels.map((points) => + inferOutsideCircularStyle('outside', points, value, coordinate), + ); + const { width, height } = coordinate.getOptions(); + const left = computed.filter((d) => d.x < width / 2); + const right = computed.filter((d) => d.x >= width / 2); + const center = coordinate.getCenter(); + const extendedOptions = { ...options, height, center }; + hideAndDodgeYAndMoveX(left, { ...extendedOptions, left: true }); + hideAndDodgeYAndMoveX(right, { ...extendedOptions, left: false }); + computed.forEach((style, i) => styleByPoints.set(labels[i], style)); + return styleByPoints.get(points); +} + export function outside( position: LabelPosition, points: Vector2[], value: Record, coordinate: Coordinate, + options: Record, + labels: Vector2[][], ) { const { bounds } = value; // When bounds.length = 1 @@ -111,10 +141,19 @@ export function outside( return inferIdentityStyle(position, points, value, coordinate); } + if (isCircular(coordinate)) { + return inferOutsideForPie( + position, + points, + value, + coordinate, + options, + labels, + ); + } + const inferDefaultStyle = isRadial(coordinate) ? inferRadialStyle - : isCircular(coordinate) - ? inferOutsideCircularStyle : inferNonCircularStyle; return inferDefaultStyle(position, points, value, coordinate); diff --git a/src/shape/label/position/utils.ts b/src/shape/label/position/utils.ts index e19ca5a13d..db5aa0eef4 100644 --- a/src/shape/label/position/utils.ts +++ b/src/shape/label/position/utils.ts @@ -75,3 +75,27 @@ export function hideAndDodgeY( } dodgeY(filtered, options); } + +export function hideAndDodgeYAndMoveX( + unsorted: Record[], + options: Record, +) { + hideAndDodgeY(unsorted, options); + const { left, center } = options; + for (const item of unsorted) { + const r = item.radius + 4; + const dy = item.y - center[1]; // (x - cx)^2 + (y - cy)^2 = totalR^2 + const rPow2 = Math.pow(r, 2); + const dyPow2 = Math.pow(dy, 2); + const dxPow2 = Math.abs(rPow2 - dyPow2); + const dx = Math.sqrt(dxPow2); + if (left) { + // const newX = center[0] - dx; + // const offsetX = newX - item.x; + // item.connectorPoints[0][0] -= offsetX; + item.x = center[0] - dx; + } else { + item.x = center[0] + dx; + } + } +}