UNPKG

@antv/g2

Version:

the Grammar of Graphics in Javascript

130 lines (118 loc) 3.8 kB
import { groupBy, keys, map } from '@antv/util'; import { IGroup, IShape, BBox } from '../../../../dependents'; import Geometry from '../../../base'; import Element from '../../../element'; import { LabelItem } from '../../interface'; import { findLabelTextShape } from '../../util'; /** * point-adjust-position layout 的配置类型 */ export interface PointAdjustPositionLayoutCfg { offset?: number; } /** * 对同一组(相同 xField )的 Label 进行排序:第一个、最后一个、其他... * @param geometry * @param labels */ function sortLabels(geometry: Geometry, labels: IGroup[]) { const yField = geometry.getXYFields()[1]; const result: IGroup[] = []; const sortedLabels = labels.sort((left, right) => left.get('data')[yField] - left.get('data')[yField]); if (sortedLabels.length > 0) { result.push(sortedLabels.shift()); } if (sortedLabels.length > 0) { result.push(sortedLabels.pop()); } result.push(...sortedLabels); return result; } function hasSome(dones: IGroup[], current: IGroup, compare: (left: IGroup, right: IGroup) => boolean): boolean { return dones.some((done) => compare(done, current)); } /** * 计算两个矩形之间的堆叠区域面积 */ function getOverlapArea(a: BBox, b: BBox, margin = 0) { const xOverlap = Math.max( 0, Math.min(a.x + a.width + margin, b.x + b.width + margin) - Math.max(a.x - margin, b.x - margin) ); const yOverlap = Math.max( 0, Math.min(a.y + a.height + margin, b.y + b.height + margin) - Math.max(a.y - margin, b.y - margin) ); return xOverlap * yOverlap; } /** * 判断新添加的 Label 是否和已存在的发生重叠 * @param dones * @param current */ function checkShapeOverlap(dones: IGroup[], current: IGroup): boolean { return hasSome(dones, current, (left, right) => { const leftText = findLabelTextShape(left); const rightText = findLabelTextShape(right); return getOverlapArea(leftText.getCanvasBBox(), rightText.getCanvasBBox(), 2) > 0; }); } /** * 适用于 point geometry 的数据标签位置自动调整布局方法 * @param items * @param labels * @param shapes * @param region * @param cfg */ export function pathAdjustPosition( items: LabelItem[], labels: IGroup[], shapes: IShape[] | IGroup[], region: BBox, cfg: PointAdjustPositionLayoutCfg ): void { if (shapes.length === 0) { return; } const element: Element = shapes[0]?.get('element'); const geometry: Geometry = element?.geometry; if (!geometry || ['path', 'line', 'area'].indexOf(geometry.type) < 0) { return; } const [xField, yField] = geometry.getXYFields(); const groupedLabels = groupBy(labels, (label) => label.get('data')[xField]); const dones: IGroup[] = []; const offset = (cfg && cfg.offset) || items[0]?.offset || 12; map(keys(groupedLabels).reverse(), (xValue) => { const sortedCollections = sortLabels(geometry, groupedLabels[xValue]); while (sortedCollections.length) { const current = sortedCollections.shift(); const textShape = findLabelTextShape(current); if ( hasSome( dones, current, (left, right) => left.get('data')[xField] === right.get('data')[xField] && left.get('data')[yField] === right.get('data')[yField] ) ) { // 重复位置,直接隐藏 textShape.set('visible', false); continue; } const upFail = checkShapeOverlap(dones, current); let downFail: boolean = false; if (upFail) { textShape.attr('y', textShape.attr('y') + 2 * offset); downFail = checkShapeOverlap(dones, current); } if (downFail) { textShape.set('visible', false); continue; } dones.push(current); } }); }