import type { AABB, TextStyleProps } from '@antv/g'; import type { PathArray } from '@antv/util'; import { isEqual } from '@antv/util'; import type { CardinalPlacement, Point } from '../types'; import { pathToPoints } from './path'; import { findNearestLine, findNearestPointOnLine } from './point'; import { getXYByPlacement } from './position'; /** * 计算文本位置样式 * * Calculate text position style * @param bounds - 外包围盒 | contour bounds * @param placement - 位置 | placement * @param offsetX - x轴偏移 | x-axis offset * @param offsetY - y轴偏移 | y-axis offset * @param closeToContour - 标签位置是否贴合轮廓 | whether the label position is close to the contour * @param path - 路径 | path * @param autoRotate - 是否跟随轮廓旋转 | whether to rotate with the contour * @returns 文本样式 | text style */ export function getPolygonTextStyleByPlacement( bounds: AABB, placement: CardinalPlacement | 'center', offsetX: number, offsetY: number, closeToContour: boolean, path: PathArray | string, autoRotate: boolean, ) { const [x, y] = getXYByPlacement(bounds, placement); const style: Partial = { textAlign: placement === 'left' ? 'right' : placement === 'right' ? 'left' : 'center', textBaseline: placement === 'top' ? 'bottom' : placement === 'bottom' ? 'top' : 'middle', transform: [['translate', x + offsetX, y + offsetY]], }; if (placement === 'center' || !closeToContour) return style; const points = pathToPoints(path); if (!points || points.length <= 3) return style; const lines = points .map((point, index) => { const p1 = point; const p2 = points[(index + 1) % points.length]; if (isEqual(p1, p2)) return null; return [p1, p2]; }) .filter(Boolean) as [Point, Point][]; const line = findNearestLine([x, y], lines); const intersection = findNearestPointOnLine([x, y], line); if (intersection && line) { style.transform = [['translate', intersection[0] + offsetX, intersection[1] + offsetY]]; if (autoRotate) { const angle = Math.atan((line[0][1] - line[1][1]) / (line[0][0] - line[1][0])); style.transform.push(['rotate', (angle / Math.PI) * 180]); style.textAlign = 'center'; if (placement === 'right' || placement === 'left') { if (angle > 0) { style.textBaseline = placement === 'right' ? 'bottom' : 'top'; } else { style.textBaseline = placement === 'right' ? 'top' : 'bottom'; } } } } return style; }