From a58a4d3668ab86206a9ee6f52bdfc0790dd89d84 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Tue, 9 Dec 2025 16:01:29 +0800 Subject: [PATCH 1/3] feat: optimiza label overlap --- .../src/axis/overlap/auto-rotate.ts | 22 ++++++++-- .../src/label/overlap/place.ts | 44 ++++++++++++++----- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/packages/vrender-components/src/axis/overlap/auto-rotate.ts b/packages/vrender-components/src/axis/overlap/auto-rotate.ts index 8f4440e2c..665f2e5df 100644 --- a/packages/vrender-components/src/axis/overlap/auto-rotate.ts +++ b/packages/vrender-components/src/axis/overlap/auto-rotate.ts @@ -28,12 +28,28 @@ export function autoRotate(items: IText[], rotateConfig: RotateConfig) { let i = 0; let n = 0; - if (labelRotateAngle && labelRotateAngle.length > 0) { - n = labelRotateAngle.length; + + let testAngle = labelRotateAngle; + if (items.length > 2) { + // 大量数据下性能优化 + // 标签过多时无论如何旋转都无法展示全部标签,通过逻辑减少尝试次数 + if (orient === 'bottom' || orient === 'top') { + if (Math.abs(items[1].attribute.x - items[0].attribute.x) < items[0].attribute.fontSize / 2) { + testAngle = [labelRotateAngle[labelRotateAngle.length - 1]]; + } + } else { + if (Math.abs(items[1].attribute.y - items[0].attribute.y) < items[0].attribute.fontSize / 2) { + testAngle = [labelRotateAngle[labelRotateAngle.length - 1]]; + } + } + } + + if (testAngle && testAngle.length > 0) { + n = testAngle.length; } while (i < n) { - const angle = labelRotateAngle[i++]; + const angle = testAngle[i++]; items.forEach(item => { // item.angle = angle; item.attribute.angle = degreeToRadian(angle); diff --git a/packages/vrender-components/src/label/overlap/place.ts b/packages/vrender-components/src/label/overlap/place.ts index aa9f3260f..d848949ac 100644 --- a/packages/vrender-components/src/label/overlap/place.ts +++ b/packages/vrender-components/src/label/overlap/place.ts @@ -78,6 +78,9 @@ export function canPlaceInside(textBound: IBoundsLike, shapeBound: IAABBBounds) return shapeBound.encloses(textBound); } +/** + * 通过预先获取一次文本的 AABB 边界,按候选坐标计算平移后的边界进行碰撞判定,避免反复更新文本属性。 + */ export function placeToCandidates( $: BitmapTool, bitmap: Bitmap, @@ -88,19 +91,40 @@ export function placeToCandidates( changePosition = false ): PointLocationCfg | false { const validCandidates = candidates.filter(candidate => isValid(candidate)); + + const curX = (text.attribute.x as number) ?? 0; + const curY = (text.attribute.y as number) ?? 0; + const base = text.AABBBounds; + const candidateBounds = { + x1: base.x1, + x2: base.x2, + y1: base.y1, + y2: base.y2 + }; + + let lastCandidate: PointLocationCfg | undefined; for (let i = 0; i < validCandidates.length; i++) { - let measureText; - if (changePosition) { - measureText = text; - } else { - measureText = text.clone(); + const c = validCandidates[i]; + lastCandidate = c; + const nx = (c.x as number) ?? curX; + const ny = (c.y as number) ?? curY; + const dx = nx - curX; + const dy = ny - curY; + + candidateBounds.x1 = base.x1 + dx; + candidateBounds.x2 = base.x2 + dx; + candidateBounds.y1 = base.y1 + dy; + candidateBounds.y2 = base.y2 + dy; + + if (canPlace($, bitmap, candidateBounds, clampForce, pad)) { + bitmap.setRange(boundToRange($, candidateBounds, true)); + // 成功时外部会设置位置,不需要在这里设置 + return c; } - measureText.setAttributes(validCandidates[i]); + } - if (canPlace($, bitmap, measureText.AABBBounds, clampForce, pad)) { - bitmap.setRange(boundToRange($, measureText.AABBBounds, true)); - return validCandidates[i]; - } + if (changePosition && lastCandidate) { + text.setAttributes(lastCandidate); } return false; } From 6377d62655736c57c86bee1880fd8fb7ea19f74e Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Tue, 9 Dec 2025 16:06:30 +0800 Subject: [PATCH 2/3] docs: update changlog of rush --- .../feat-otimize-label-overlap_2025-12-09-08-06.json | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 common/changes/@visactor/vrender-components/feat-otimize-label-overlap_2025-12-09-08-06.json diff --git a/common/changes/@visactor/vrender-components/feat-otimize-label-overlap_2025-12-09-08-06.json b/common/changes/@visactor/vrender-components/feat-otimize-label-overlap_2025-12-09-08-06.json new file mode 100644 index 000000000..8b79034d2 --- /dev/null +++ b/common/changes/@visactor/vrender-components/feat-otimize-label-overlap_2025-12-09-08-06.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "comment": "feat: optimiza label overlap\n\n", + "type": "none", + "packageName": "@visactor/vrender-components" + } + ], + "packageName": "@visactor/vrender-components", + "email": "lixuef1313@163.com" +} \ No newline at end of file From d1b45ce92c47b932c70c73c27928e9121cc81cc5 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Tue, 9 Dec 2025 16:51:16 +0800 Subject: [PATCH 3/3] fix: fix bug of overlap is undefiend --- packages/vrender-components/src/label/base.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/vrender-components/src/label/base.ts b/packages/vrender-components/src/label/base.ts index d70d759f7..e9201f4ed 100644 --- a/packages/vrender-components/src/label/base.ts +++ b/packages/vrender-components/src/label/base.ts @@ -215,7 +215,7 @@ export class LabelBase extends AnimateComponent { const filteredLabels: (IText | IRichText)[] = []; const overlapLabels: (IText | IRichText)[] = labels; - if (!isBoolean(overlap) && isFunction(overlap.filterBeforeOverlap)) { + if (!isBoolean(overlap) && isFunction(overlap?.filterBeforeOverlap)) { const getRelatedGraphic = this.getRelatedGraphic.bind(this); labels.forEach(label => { if (overlap.filterBeforeOverlap(label, getRelatedGraphic, this)) {