From e245a04824ea81b1b74cbefbe21630f1ace40317 Mon Sep 17 00:00:00 2001 From: xiaoluoHe Date: Mon, 31 Jul 2023 10:30:43 +0800 Subject: [PATCH 1/2] Revert "Revert "feat: label support custom layout function"" This reverts commit 8d83dca2700161996415df6afd051c979dc33fd8. --- ...-label_custom_layout_2023-07-21-07-11.json | 10 + .../__tests__/browser/examples/label-line.ts | 4 +- packages/vrender-components/src/label/base.ts | 268 ++++++++++-------- .../src/label/overlap/place.ts | 38 ++- packages/vrender-components/src/label/rect.ts | 10 +- .../vrender-components/src/label/symbol.ts | 10 +- packages/vrender-components/src/label/type.ts | 42 ++- 7 files changed, 226 insertions(+), 156 deletions(-) create mode 100644 common/changes/@visactor/vrender-components/feat-label_custom_layout_2023-07-21-07-11.json diff --git a/common/changes/@visactor/vrender-components/feat-label_custom_layout_2023-07-21-07-11.json b/common/changes/@visactor/vrender-components/feat-label_custom_layout_2023-07-21-07-11.json new file mode 100644 index 000000000..8a79f6a0a --- /dev/null +++ b/common/changes/@visactor/vrender-components/feat-label_custom_layout_2023-07-21-07-11.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vrender-components", + "comment": "feat(component): label component support custom layout and dataFilter", + "type": "patch" + } + ], + "packageName": "@visactor/vrender-components" +} \ No newline at end of file diff --git a/packages/vrender-components/__tests__/browser/examples/label-line.ts b/packages/vrender-components/__tests__/browser/examples/label-line.ts index ea1d04135..d0bc18644 100644 --- a/packages/vrender-components/__tests__/browser/examples/label-line.ts +++ b/packages/vrender-components/__tests__/browser/examples/label-line.ts @@ -2943,8 +2943,10 @@ function createContent(stage: Stage) { animation: { // mode: 'after' }, + dataFilter: data => { + return data.filter(item => Number(item.text) > 0.59).reverse(); + }, overlap: { - enable: false, avoidBaseMark: true, size: { width: 1036.2320098876953, diff --git a/packages/vrender-components/src/label/base.ts b/packages/vrender-components/src/label/base.ts index 6d1b8e901..db6dcc095 100644 --- a/packages/vrender-components/src/label/base.ts +++ b/packages/vrender-components/src/label/base.ts @@ -4,15 +4,15 @@ import type { IGroup, Text, IGraphic, IText, FederatedPointerEvent, IColor } from '@visactor/vrender'; import { createText, IncreaseCount, AttributeUpdateType } from '@visactor/vrender'; import type { IBoundsLike } from '@visactor/vutils'; -import { isFunction, isValidNumber, isEmpty } from '@visactor/vutils'; +import { isFunction, isValidNumber, isEmpty, isValid } from '@visactor/vutils'; import { AbstractComponent } from '../core/base'; import type { PointLocationCfg } from '../core/type'; import { labelSmartInvert } from '../util/labelSmartInvert'; import { traverseGroup } from '../util'; import { StateValue } from '../constant'; import type { Bitmap } from './overlap'; -import { bitmapTool, boundToRange, canPlace, canPlaceInside, place } from './overlap'; -import type { BaseLabelAttrs, OverlapAttrs, ILabelGraphicAttribute, ILabelAnimation } from './type'; +import { bitmapTool, boundToRange, canPlace, canPlaceInside, clampText, place } from './overlap'; +import type { BaseLabelAttrs, OverlapAttrs, ILabelAnimation, LabelItem, SmartInvertAttrs } from './type'; import { DefaultLabelAnimation, getAnimationAttributes } from './animate/animate'; export abstract class LabelBase extends AbstractComponent { @@ -31,11 +31,9 @@ export abstract class LabelBase extends AbstractCompon this._bmpTool = bmpTool; } - protected _relationMap: Map; + protected _graphicToText: Map; - protected _prevRelationMap: Map; - - protected _textMap: Map; + protected _idToGraphic: Map; onAfterLabelOverlap?: (bitmap: Bitmap) => void; @@ -49,13 +47,39 @@ export abstract class LabelBase extends AbstractCompon graphicBounds: IBoundsLike, position?: BaseLabelAttrs['position'], offset?: number - ): Partial | undefined; + ): { x: number; y: number } | undefined; protected render() { - const currentBaseMarks = this._checkMarks(); - const labels = this.layout(currentBaseMarks); + this._prepare(); + + const { overlap, smartInvert, dataFilter, customLayoutFunc, customOverlapFunc } = this.attribute; + let data = this.attribute.data; + + if (isFunction(dataFilter)) { + data = dataFilter(data); + } + + let labels: IText[]; + + if (isFunction(customLayoutFunc)) { + labels = customLayoutFunc(data, (d: LabelItem) => this._idToGraphic.get(d.id)); + } else { + // 根据关联图元和配置的position计算标签坐标 + labels = this.layout(data); + + if (isFunction(customOverlapFunc)) { + labels = customOverlapFunc(labels as Text[], (d: LabelItem) => this._idToGraphic.get(d.id)); + } else { + // 防重叠逻辑 + if (overlap !== false) { + labels = this._overlapping(labels); + } + } + } - this._smartInvert(labels); + if (smartInvert !== false) { + this._smartInvert(labels); + } this._renderLabels(labels); } @@ -140,14 +164,14 @@ export abstract class LabelBase extends AbstractCompon } }; - private _createLabelText(attributes: ILabelGraphicAttribute) { + private _createLabelText(attributes: LabelItem) { const text = createText(attributes); this._bindEvent(text); this._setStates(text); return text; } - private _checkMarks() { + private _prepare() { const baseMarks = this.getBaseMarks(); const currentBaseMarks: IGraphic[] = []; baseMarks.forEach(mark => { @@ -155,80 +179,79 @@ export abstract class LabelBase extends AbstractCompon currentBaseMarks.push(mark); } }); - this._prevRelationMap = new Map(this._relationMap); - this._relationMap?.clear(); - return currentBaseMarks; - } - protected layout(currentMarks?: IGraphic[]): ILabelGraphicAttribute[] { - const { textStyle, position, offset } = this.attribute as BaseLabelAttrs; - let { data } = this.attribute as BaseLabelAttrs; - if (isFunction(data)) { - data = data({}); - } - if (!data || data.length === 0) { - return []; + this._idToGraphic?.clear(); + this._baseMarks = currentBaseMarks; + + if (!currentBaseMarks || currentBaseMarks.length === 0) { + return; } - let labels: ILabelGraphicAttribute[] = []; + const { data } = this.attribute; - if (isFunction(this.attribute.sort) && currentMarks && currentMarks.length) { - currentMarks = currentMarks.sort(this.attribute.sort); + if (!data || data.length === 0) { + return; } - if (!this._relationMap) { - this._relationMap = new Map(); + if (!this._idToGraphic) { + this._idToGraphic = new Map(); } + // generate id mapping before data filter + for (let i = 0; i < currentBaseMarks.length; i++) { + const textData = data[i]; + const baseMark = currentBaseMarks[i] as IGraphic; + if (textData && baseMark) { + if (!isValid(textData.id)) { + textData.id = `vrender-component-${this.name}-${i}`; + } + this._idToGraphic.set(textData.id, baseMark); + } + } + } + + protected layout(data: LabelItem[] = []): IText[] { + const { textStyle = {}, position, offset } = this.attribute; + const labels = []; - // 默认根据 index 顺序排序 for (let i = 0; i < data.length; i++) { const textData = data[i]; - const baseMark = currentMarks?.[i] as IGraphic; + const baseMark = this._idToGraphic.get(textData.id); + const labelAttribute = { ...textStyle, - ...textData, - _relatedIndex: i + ...textData }; - this._relationMap.set(i, baseMark); - - if (textData) { - const text = createText(labelAttribute); - text.update(); - const textBounds = this.getGraphicBounds(text); - const graphicBounds = this.getGraphicBounds(baseMark, { x: textData.x as number, y: textData.y as number }); - - const textAttributes = this.labeling( - textBounds, - graphicBounds, - isFunction(position) ? position(textData) : position, - offset - ); - - if (!textAttributes) { - continue; - } - - labelAttribute.x = textAttributes.x; - labelAttribute.y = textAttributes.y; - - labels.push(labelAttribute); + const text = this._createLabelText(labelAttribute); + const textBounds = this.getGraphicBounds(text); + const graphicBounds = this.getGraphicBounds(baseMark, { x: textData.x as number, y: textData.y as number }); + const textLocation = this.labeling( + textBounds, + graphicBounds, + isFunction(position) ? position(textData) : position, + offset + ); + if (!textLocation) { + continue; } - } + labelAttribute.x = textLocation.x; + labelAttribute.y = textLocation.y; - this._baseMarks = currentMarks as IGraphic[]; - if (this.attribute.overlap !== false) { - labels = this.overlapping(labels, this.attribute.overlap as OverlapAttrs); + text.setAttributes(textLocation); + labels.push(text); } return labels; } - protected overlapping(labels: ILabelGraphicAttribute[], option: OverlapAttrs = {}) { + protected _overlapping(labels: IText[]) { if (labels.length === 0) { return []; } - const result: ILabelGraphicAttribute[] = []; + const option = this.attribute.overlap as OverlapAttrs; + + const result: IText[] = []; const baseMarkGroup = this.getBaseMarkGroup(); + const size = option.size ?? { width: baseMarkGroup?.AABBBounds.width() ?? 0, height: baseMarkGroup?.AABBBounds.height() ?? 0 @@ -253,8 +276,8 @@ export abstract class LabelBase extends AbstractCompon if (labels[i].visible === false) { continue; } - const text = createText(labels[i]) as Text; - const baseMark = this._baseMarks?.[i]; + const text = labels[i] as IText; + const baseMark = this._idToGraphic.get((text.attribute as LabelItem).id); text.update(); // 默认位置可以放置 @@ -262,13 +285,31 @@ export abstract class LabelBase extends AbstractCompon // 如果配置了限制在图形内部,需要提前判断; if (!checkBounds) { bitmap.setRange(boundToRange(bmpTool, text.AABBBounds, true)); - result.push({ ...text.attribute }); + result.push(text); continue; } if (checkBounds && baseMark?.AABBBounds && canPlaceInside(text.AABBBounds, baseMark?.AABBBounds)) { bitmap.setRange(boundToRange(bmpTool, text.AABBBounds, true)); - result.push({ ...text.attribute }); + result.push(text); + continue; + } + } + + // 尝试向内挤压 + if (clampForce) { + const { dx = 0, dy = 0 } = clampText(text, bmpTool.width, bmpTool.height); + if ( + !(dx === 0 && dy === 0) && + canPlace(bmpTool, bitmap, { + x1: text.AABBBounds.x1 + dx, + x2: text.AABBBounds.x2 + dx, + y1: text.AABBBounds.y1 + dy, + y2: text.AABBBounds.y2 + dy + }) + ) { + text.setAttributes({ x: text.attribute.x + dx, y: text.attribute.y + dy }); + result.push(text); continue; } } @@ -281,21 +322,18 @@ export abstract class LabelBase extends AbstractCompon bitmap, strategy[j], this.attribute, - text, + text as Text, this.getGraphicBounds(baseMark, labels[i]), this.labeling ); if (hasPlace !== false) { - result.push({ - ...text.attribute, - x: hasPlace.x, - y: hasPlace.y - }); + text.setAttributes({ x: hasPlace.x, y: hasPlace.y }); + result.push(text); break; } } - !hasPlace && !hideOnHit && result.push({ ...text.attribute }); + !hasPlace && !hideOnHit && result.push(text); } if (isFunction(this.onAfterLabelOverlap)) { @@ -340,7 +378,7 @@ export abstract class LabelBase extends AbstractCompon ); } - protected _renderLabels(labels: ILabelGraphicAttribute[]) { + protected _renderLabels(labels: IText[]) { const animationConfig = (this.attribute.animation ?? {}) as ILabelAnimation; const disableAnimation = this._enableAnimation === false || (animationConfig as unknown as boolean) === false; const mode = animationConfig.mode ?? DefaultLabelAnimation.mode; @@ -349,19 +387,16 @@ export abstract class LabelBase extends AbstractCompon const delay = animationConfig.delay ?? 0; const currentTextMap = new Map(); - const prevTextMap = this._textMap || new Map(); + const prevTextMap = this._graphicToText || new Map(); const texts = [] as IText[]; - - labels.forEach((label, index) => { - const text = this._createLabelText(label); - const relatedGraphic = this._relationMap.get(label._relatedIndex); + labels.forEach((text, index) => { + const relatedGraphic = this._idToGraphic.get((text.attribute as LabelItem).id); const state = prevTextMap?.get(relatedGraphic) ? 'update' : 'enter'; - if (state === 'enter') { texts.push(text); currentTextMap.set(relatedGraphic, text); if (!disableAnimation && relatedGraphic) { - const { from, to } = getAnimationAttributes(label, 'fadeIn'); + const { from, to } = getAnimationAttributes(text.attribute, 'fadeIn'); this.add(text); relatedGraphic.onAnimateBind = () => { text.setAttributes(from); @@ -419,7 +454,7 @@ export abstract class LabelBase extends AbstractCompon } }); - this._textMap = currentTextMap; + this._graphicToText = currentTextMap; } protected _afterRelatedGraphicAttributeUpdate( @@ -486,20 +521,17 @@ export abstract class LabelBase extends AbstractCompon return listener; } - protected _smartInvert(labels: ILabelGraphicAttribute[]) { - if (this.attribute.smartInvert === false) { - return; - } + protected _smartInvert(labels: IText[]) { + const option = (this.attribute.smartInvert || {}) as SmartInvertAttrs; + const { textType, contrastRatiosThreshold, alternativeColors } = option; + for (let i = 0; i < labels.length; i++) { - const label = labels?.[i] as ILabelGraphicAttribute; + const label = labels[i]; if (!label) { continue; } - - const isInside = canPlaceInside( - createText(label).AABBBounds, - this._relationMap.get(label._relatedIndex)?.AABBBounds - ); + const baseMark = this._idToGraphic.get((label.attribute as LabelItem).id); + const isInside = canPlaceInside(label.AABBBounds, baseMark?.AABBBounds); /** * stroke 的处理逻辑 * 1. 当文本在图元内部时,有两种情况: @@ -509,50 +541,42 @@ export abstract class LabelBase extends AbstractCompon * - a. 未设置stroke:此时设置strokeColor为backgroundColor。labelFill为前景色,labelStroke填充色为背景色。避免文字一半在图元内部,一半在图元外部时,在图元外部文字不可见。 * - b. 设置了stroke:保持strokeColor。labelFill为前景色,labelStroke填充色为背景色。 */ - if (label.stroke && label.lineWidth > 0) { + if (label.attribute.stroke && label.attribute.lineWidth > 0) { /** * 1-b, 2-b * 若label存在stroke,label填充色为前景色,label描边色为背景色 * WCAG 2 字母周围的文本发光/光晕可用作背景颜色 */ - label.fill = labelSmartInvert( - label.fill as IColor, - label.stroke as IColor, - this.attribute.smartInvert?.textType, - this.attribute.smartInvert?.contrastRatiosThreshold, - this.attribute.smartInvert?.alternativeColors - ); + label.setAttributes({ + fill: labelSmartInvert( + label.attribute.fill as IColor, + label.attribute.stroke as IColor, + textType, + contrastRatiosThreshold, + alternativeColors + ) + }); } else if (isInside) { /** * 1-a * label在图元内部时,label填充色为前景色,baseMark填充色为背景色 */ - const baseMark = this._relationMap.get(label._relatedIndex); const backgroundColor = baseMark.attribute.fill as IColor; - const foregroundColor = label.fill as IColor; - label.fill = labelSmartInvert( - foregroundColor, - backgroundColor, - this.attribute.smartInvert?.textType, - this.attribute.smartInvert?.contrastRatiosThreshold, - this.attribute.smartInvert?.alternativeColors - ); - } else if (label.lineWidth > 0) { + const foregroundColor = label.attribute.fill as IColor; + label.setAttributes({ + fill: labelSmartInvert(foregroundColor, backgroundColor, textType, contrastRatiosThreshold, alternativeColors) + }); + } else if (label.attribute.lineWidth > 0) { /** * 2-a * 当文本在图元外部时,设置strokeColor为backgroundColor。labelFill为前景色,labelStroke填充色为背景色。 */ - const baseMark = this._relationMap.get(label._relatedIndex); - label.stroke = baseMark.attribute.fill; - const backgroundColor = label.stroke as IColor; - const foregroundColor = label.fill as IColor; - label.fill = labelSmartInvert( - foregroundColor, - backgroundColor, - this.attribute.smartInvert?.textType, - this.attribute.smartInvert?.contrastRatiosThreshold, - this.attribute.smartInvert?.alternativeColors - ); + const backgroundColor = label.attribute.stroke as IColor; + const foregroundColor = label.attribute.fill as IColor; + label.setAttributes({ + stroke: baseMark.attribute.fill, + fill: labelSmartInvert(foregroundColor, backgroundColor, textType, contrastRatiosThreshold, alternativeColors) + }); } } } diff --git a/packages/vrender-components/src/label/overlap/place.ts b/packages/vrender-components/src/label/overlap/place.ts index a8f5a7ee0..c7327e39d 100644 --- a/packages/vrender-components/src/label/overlap/place.ts +++ b/packages/vrender-components/src/label/overlap/place.ts @@ -1,10 +1,12 @@ -import { Text } from '@visactor/vrender'; -import { IAABBBounds, IBoundsLike, isFunction } from '@visactor/vutils'; -import { PointLocationCfg } from '../../core/type'; +import type { IText, Text } from '@visactor/vrender'; +import type { IAABBBounds, IBoundsLike } from '@visactor/vutils'; +import { isFunction } from '@visactor/vutils'; +import type { PointLocationCfg } from '../../core/type'; import type { LabelBase } from '../base'; import type { BaseLabelAttrs, OverlapAttrs, Strategy } from '../type'; import type { Bitmap } from './bitmap'; -import { BitmapTool, boundToRange } from './scaler'; +import type { BitmapTool } from './scaler'; +import { boundToRange } from './scaler'; /** * 防重叠逻辑参考 https://github.com/vega/vega/ @@ -139,3 +141,31 @@ export function defaultLabelPosition(type?: string) { return DefaultPositions; } } + +export function clampText(text: IText, width: number, height: number) { + const { x1, x2, y1, y2 } = text.AABBBounds; + const minX = Math.min(x1, x2); + const maxX = Math.max(x1, x2); + + const minY = Math.min(y1, y2); + const maxY = Math.max(y1, y2); + + let dx = 0; + let dy = 0; + + // x 方向 + if (minX < 0 && maxX - minX <= width) { + dx = -minX; + } else if (maxX > width && minX - (maxX - width) >= 0) { + dx = width - maxX; + } + + // y 方向 + if (minY < 0 && maxY - minY <= height) { + dy = -minY; + } else if (maxY > height && minY - (maxY - height) >= 0) { + dy = height - maxY; + } + + return { dx, dy }; +} diff --git a/packages/vrender-components/src/label/rect.ts b/packages/vrender-components/src/label/rect.ts index c05ff43e7..4f926a592 100644 --- a/packages/vrender-components/src/label/rect.ts +++ b/packages/vrender-components/src/label/rect.ts @@ -1,4 +1,5 @@ -import { merge, IBoundsLike } from '@visactor/vutils'; +import type { IBoundsLike } from '@visactor/vutils'; +import { merge } from '@visactor/vutils'; import type { ITextGraphicAttribute } from '@visactor/vrender'; import type { RectLabelAttrs } from './type'; import { LabelBase } from './base'; @@ -23,12 +24,7 @@ export class RectLabel extends LabelBase { super(merge({}, RectLabel.defaultAttributes, attributes)); } - protected labeling( - textBounds: IBoundsLike, - graphicBounds: IBoundsLike, - position = 'top', - offset = 0 - ): Partial | undefined { + protected labeling(textBounds: IBoundsLike, graphicBounds: IBoundsLike, position = 'top', offset = 0) { if (!textBounds || !graphicBounds) { return; } diff --git a/packages/vrender-components/src/label/symbol.ts b/packages/vrender-components/src/label/symbol.ts index 2c199aaf2..c3f915f0d 100644 --- a/packages/vrender-components/src/label/symbol.ts +++ b/packages/vrender-components/src/label/symbol.ts @@ -1,4 +1,5 @@ -import { merge, IBoundsLike } from '@visactor/vutils'; +import type { IBoundsLike } from '@visactor/vutils'; +import { merge } from '@visactor/vutils'; import type { ITextGraphicAttribute } from '@visactor/vrender'; import type { SymbolLabelAttrs } from './type'; import { LabelBase } from './base'; @@ -23,12 +24,7 @@ export class SymbolLabel extends LabelBase { super(merge({}, SymbolLabel.defaultAttributes, attributes)); } - protected labeling( - textBounds: IBoundsLike, - graphicBounds: IBoundsLike, - position = 'top', - offset = 0 - ): Partial | undefined { + protected labeling(textBounds: IBoundsLike, graphicBounds: IBoundsLike, position = 'top', offset = 0) { if (!textBounds) { return; } diff --git a/packages/vrender-components/src/label/type.ts b/packages/vrender-components/src/label/type.ts index 86fdaad57..105273378 100644 --- a/packages/vrender-components/src/label/type.ts +++ b/packages/vrender-components/src/label/type.ts @@ -1,5 +1,4 @@ -import type { EasingType, IGroupGraphicAttribute, ITextGraphicAttribute } from '@visactor/vrender'; -import type { Bounds } from '@visactor/vutils'; +import type { EasingType, IGraphic, IGroupGraphicAttribute, ITextGraphicAttribute, Text } from '@visactor/vrender'; export type LabelItemStateStyle = { hover?: T; @@ -8,8 +7,20 @@ export type LabelItemStateStyle = { selected_reverse?: T; }; +export type LabelItem = { + // 用于动画 + id?: string; + // 原始数据 + data?: any; + [key: string]: any; +} & ITextGraphicAttribute; + export interface BaseLabelAttrs extends IGroupGraphicAttribute { type: string; + /** + * 图元 group 名称 + */ + baseMarkGroupName: string; /** * 是否开启选中交互 * @default false @@ -23,15 +34,7 @@ export interface BaseLabelAttrs extends IGroupGraphicAttribute { /** * 标签数据 */ - data: Functional<(ITextGraphicAttribute & Record)[]>; - - sort?: (graphicA: any, graphicB: any) => number; - - /** - * 图元 group 名称 - * 如果不配置,需要在 data 里指定标签定位 x/y - */ - baseMarkGroupName?: string; + data: LabelItem[]; /** 文本样式,优先级低于 data */ textStyle?: Partial; @@ -55,6 +58,19 @@ export interface BaseLabelAttrs extends IGroupGraphicAttribute { /** 动画配置 */ animation?: ILabelAnimation | false; + + // 排序 or 删减 + dataFilter?: (data: LabelItem[]) => LabelItem[]; + + /** 自定义布局函数 + * @description 当配置了 customLayoutFunc 后,默认布局和防重叠逻辑将不再生效。(overlap/position/offset不生效) + */ + customLayoutFunc?: (data: LabelItem[], getRelatedGraphic: (data: LabelItem) => IGraphic) => Text[]; + + /** 自定义标签躲避函数 + * @description 当配置了 customOverlapFunc 后,会根据 position 和 offset 进行初始布局。配置的防重叠逻辑(overlap)不生效。 + */ + customOverlapFunc?: (label: Text[], getRelatedGraphic: (data: LabelItem) => IGraphic) => Text[]; } export interface OverlapAttrs { @@ -205,10 +221,6 @@ export interface DataLabelAttrs extends IGroupGraphicAttribute { export type Functional = T | ((data: any) => T); -export interface ILabelGraphicAttribute extends ITextGraphicAttribute { - _relatedIndex?: number; -} - export interface ILabelAnimation { mode?: 'same-time' | 'after' | 'after-all'; duration?: number; From 2a13907974617bc94e2209f40796f967866a76c5 Mon Sep 17 00:00:00 2001 From: xiaoluoHe Date: Mon, 31 Jul 2023 10:41:11 +0800 Subject: [PATCH 2/2] refactor: adjust the execution timing of label clampForce --- packages/vrender-components/src/label/base.ts | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/packages/vrender-components/src/label/base.ts b/packages/vrender-components/src/label/base.ts index db6dcc095..6586bc749 100644 --- a/packages/vrender-components/src/label/base.ts +++ b/packages/vrender-components/src/label/base.ts @@ -296,24 +296,6 @@ export abstract class LabelBase extends AbstractCompon } } - // 尝试向内挤压 - if (clampForce) { - const { dx = 0, dy = 0 } = clampText(text, bmpTool.width, bmpTool.height); - if ( - !(dx === 0 && dy === 0) && - canPlace(bmpTool, bitmap, { - x1: text.AABBBounds.x1 + dx, - x2: text.AABBBounds.x2 + dx, - y1: text.AABBBounds.y1 + dy, - y2: text.AABBBounds.y2 + dy - }) - ) { - text.setAttributes({ x: text.attribute.x + dx, y: text.attribute.y + dy }); - result.push(text); - continue; - } - } - let hasPlace: ReturnType = false; // 发生碰撞,根据策略寻找可放置的位置 for (let j = 0; j < strategy.length; j++) { @@ -333,6 +315,25 @@ export abstract class LabelBase extends AbstractCompon } } + // 尝试向内挤压 + if (clampForce) { + const { dx = 0, dy = 0 } = clampText(text, bmpTool.width, bmpTool.height); + if ( + !(dx === 0 && dy === 0) && + canPlace(bmpTool, bitmap, { + x1: text.AABBBounds.x1 + dx, + x2: text.AABBBounds.x2 + dx, + y1: text.AABBBounds.y1 + dy, + y2: text.AABBBounds.y2 + dy + }) + ) { + text.setAttributes({ x: text.attribute.x + dx, y: text.attribute.y + dy }); + bitmap.setRange(boundToRange(bmpTool, text.AABBBounds, true)); + result.push(text); + continue; + } + } + !hasPlace && !hideOnHit && result.push(text); }