diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 08804f11a..cb449e911 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -12,8 +12,8 @@ importers: '@esbuild-plugins/node-modules-polyfill': 0.1.4 '@internal/eslint-config': workspace:* '@internal/ts-config': workspace:* - '@visactor/vrender': workspace:0.13.6 - '@visactor/vrender-kits': workspace:0.12.10 + '@visactor/vrender': workspace:0.13.7 + '@visactor/vrender-kits': workspace:0.12.11 '@visactor/vutils': ~0.13.0 canvas: 2.11.2 d3-scale-chromatic: ^3.0.0 @@ -55,7 +55,7 @@ importers: '@types/react': 16.9.49 '@types/react-dom': 16.9.8 '@types/react-reconciler': ^0.28.2 - '@visactor/vrender': workspace:0.13.6 + '@visactor/vrender': workspace:0.13.7 '@visactor/vutils': ~0.13.0 '@vitejs/plugin-react': 3.1.0 eslint: ~8.18.0 @@ -93,8 +93,8 @@ importers: '@rushstack/eslint-patch': ~1.1.4 '@types/react': 16.9.49 '@types/react-dom': 16.9.8 - '@visactor/react-vrender': workspace:0.12.10 - '@visactor/vrender': workspace:0.13.6 + '@visactor/react-vrender': workspace:0.12.11 + '@visactor/vrender': workspace:0.13.7 '@visactor/vutils': ~0.13.0 '@vitejs/plugin-react': 3.1.0 eslint: ~8.18.0 @@ -184,7 +184,7 @@ importers: '@internal/ts-config': workspace:* '@rushstack/eslint-patch': ~1.1.4 '@types/jest': ^26.0.0 - '@visactor/vrender': workspace:0.13.6 + '@visactor/vrender': workspace:0.13.7 '@visactor/vscale': ~0.13.0 '@visactor/vutils': ~0.13.0 eslint: ~8.18.0 @@ -224,7 +224,7 @@ importers: '@types/node-fetch': 2.6.4 '@types/react': 16.9.49 '@types/react-dom': 16.9.8 - '@visactor/vrender': workspace:0.13.6 + '@visactor/vrender': workspace:0.13.7 '@visactor/vutils': ~0.13.0 '@vitejs/plugin-react': 3.1.0 canvas: 2.11.2 diff --git a/docs/demos/package.json b/docs/demos/package.json index 8bfe9ec5f..13e165a5a 100644 --- a/docs/demos/package.json +++ b/docs/demos/package.json @@ -11,7 +11,7 @@ "devDependencies": { "@internal/eslint-config": "workspace:*", "@internal/ts-config": "workspace:*", - "@visactor/vrender-kits": "workspace:0.12.10", + "@visactor/vrender-kits": "workspace:0.12.11", "@visactor/vutils": "~0.13.0", "d3-scale-chromatic": "^3.0.0", "lodash": "4.17.21", @@ -28,6 +28,6 @@ "@antv/g": "^5.7.4" }, "dependencies": { - "@visactor/vrender": "workspace:0.13.6" + "@visactor/vrender": "workspace:0.13.7" } } diff --git a/packages/react-vrender-utils/CHANGELOG.json b/packages/react-vrender-utils/CHANGELOG.json index b16c637f8..22a438fb6 100644 --- a/packages/react-vrender-utils/CHANGELOG.json +++ b/packages/react-vrender-utils/CHANGELOG.json @@ -1,6 +1,21 @@ { "name": "@visactor/react-vrender-utils", "entries": [ + { + "version": "0.12.11", + "tag": "@visactor/react-vrender-utils_v0.12.11", + "date": "Thu, 03 Aug 2023 10:04:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@visactor/vrender\" from `0.13.6` to `0.13.7`" + }, + { + "comment": "Updating dependency \"@visactor/react-vrender\" from `0.12.10` to `0.12.11`" + } + ] + } + }, { "version": "0.12.10", "tag": "@visactor/react-vrender-utils_v0.12.10", diff --git a/packages/react-vrender-utils/CHANGELOG.md b/packages/react-vrender-utils/CHANGELOG.md index c5305f343..36f7e4dc5 100644 --- a/packages/react-vrender-utils/CHANGELOG.md +++ b/packages/react-vrender-utils/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/react-vrender-utils -This log was last generated on Wed, 02 Aug 2023 03:13:00 GMT and should not be manually modified. +This log was last generated on Thu, 03 Aug 2023 10:04:34 GMT and should not be manually modified. + +## 0.12.11 +Thu, 03 Aug 2023 10:04:34 GMT + +_Version update only_ ## 0.12.10 Wed, 02 Aug 2023 03:13:00 GMT diff --git a/packages/react-vrender-utils/package.json b/packages/react-vrender-utils/package.json index cb305d58d..ef5c0ebdc 100644 --- a/packages/react-vrender-utils/package.json +++ b/packages/react-vrender-utils/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vrender-utils", - "version": "0.12.10", + "version": "0.12.11", "description": "", "sideEffects": false, "main": "cjs/index.js", @@ -25,8 +25,8 @@ "react-dom": "^18.2.0" }, "dependencies": { - "@visactor/vrender": "workspace:0.13.6", - "@visactor/react-vrender": "workspace:0.12.10", + "@visactor/vrender": "workspace:0.13.7", + "@visactor/react-vrender": "workspace:0.12.11", "@visactor/vutils": "~0.13.0", "react-reconciler": "^0.29.0", "tslib": "^2.3.1" diff --git a/packages/react-vrender/CHANGELOG.json b/packages/react-vrender/CHANGELOG.json index 7ad969819..fa1c51263 100644 --- a/packages/react-vrender/CHANGELOG.json +++ b/packages/react-vrender/CHANGELOG.json @@ -1,6 +1,18 @@ { "name": "@visactor/react-vrender", "entries": [ + { + "version": "0.12.11", + "tag": "@visactor/react-vrender_v0.12.11", + "date": "Thu, 03 Aug 2023 10:04:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@visactor/vrender\" from `0.13.6` to `0.13.7`" + } + ] + } + }, { "version": "0.12.10", "tag": "@visactor/react-vrender_v0.12.10", diff --git a/packages/react-vrender/CHANGELOG.md b/packages/react-vrender/CHANGELOG.md index 2bcae2fdb..b1e22d768 100644 --- a/packages/react-vrender/CHANGELOG.md +++ b/packages/react-vrender/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/react-vrender -This log was last generated on Wed, 02 Aug 2023 03:13:00 GMT and should not be manually modified. +This log was last generated on Thu, 03 Aug 2023 10:04:34 GMT and should not be manually modified. + +## 0.12.11 +Thu, 03 Aug 2023 10:04:34 GMT + +_Version update only_ ## 0.12.10 Wed, 02 Aug 2023 03:13:00 GMT diff --git a/packages/react-vrender/package.json b/packages/react-vrender/package.json index d02a6631e..9efde00c1 100644 --- a/packages/react-vrender/package.json +++ b/packages/react-vrender/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/react-vrender", - "version": "0.12.10", + "version": "0.12.11", "description": "", "sideEffects": false, "main": "cjs/index.js", @@ -24,7 +24,7 @@ "react": "^18.2.0" }, "dependencies": { - "@visactor/vrender": "workspace:0.13.6", + "@visactor/vrender": "workspace:0.13.7", "@visactor/vutils": "~0.13.0", "react-reconciler": "^0.29.0", "tslib": "^2.3.1" diff --git a/packages/vrender-components/CHANGELOG.json b/packages/vrender-components/CHANGELOG.json index e28f02324..cb66fe333 100644 --- a/packages/vrender-components/CHANGELOG.json +++ b/packages/vrender-components/CHANGELOG.json @@ -1,6 +1,38 @@ { "name": "@visactor/vrender-components", "entries": [ + { + "version": "0.13.8", + "tag": "@visactor/vrender-components_v0.13.8", + "date": "Thu, 03 Aug 2023 10:04:34 GMT", + "comments": { + "patch": [ + { + "comment": "feat(component): label component support custom layout and dataFilter" + }, + { + "comment": "chore: trigger publish alpha version" + }, + { + "comment": "fix(marker): formatMethod logic should not in vrender" + }, + { + "comment": "fix(marker): formatMethod logic should not in vrender" + }, + { + "comment": "fix(marker): segement support autoRotate setting" + }, + { + "comment": "fix(marker): formatMethod logic should not in vrender" + } + ], + "dependency": [ + { + "comment": "Updating dependency \"@visactor/vrender\" from `0.13.6` to `0.13.7`" + } + ] + } + }, { "version": "0.13.7", "tag": "@visactor/vrender-components_v0.13.7", diff --git a/packages/vrender-components/CHANGELOG.md b/packages/vrender-components/CHANGELOG.md index 832f726af..91d64bc06 100644 --- a/packages/vrender-components/CHANGELOG.md +++ b/packages/vrender-components/CHANGELOG.md @@ -1,6 +1,18 @@ # Change Log - @visactor/vrender-components -This log was last generated on Wed, 02 Aug 2023 03:13:00 GMT and should not be manually modified. +This log was last generated on Thu, 03 Aug 2023 10:04:34 GMT and should not be manually modified. + +## 0.13.8 +Thu, 03 Aug 2023 10:04:34 GMT + +### Patches + +- feat(component): label component support custom layout and dataFilter +- chore: trigger publish alpha version +- fix(marker): formatMethod logic should not in vrender +- fix(marker): formatMethod logic should not in vrender +- fix(marker): segement support autoRotate setting +- fix(marker): formatMethod logic should not in vrender ## 0.13.7 Wed, 02 Aug 2023 03:13:00 GMT 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/__tests__/browser/examples/mark-area.ts b/packages/vrender-components/__tests__/browser/examples/mark-area.ts index 9737b86f9..ea9b57e7a 100644 --- a/packages/vrender-components/__tests__/browser/examples/mark-area.ts +++ b/packages/vrender-components/__tests__/browser/examples/mark-area.ts @@ -19,9 +19,6 @@ export function run() { position: guiObject.labelPos, dx: guiObject.labelDx, dy: guiObject.labelDy, - formatMethod: text => { - return text + '格式化'; - }, areaStyle: { cornerRadius: guiObject.cornerRadius } diff --git a/packages/vrender-components/__tests__/browser/examples/mark-line.ts b/packages/vrender-components/__tests__/browser/examples/mark-line.ts index 68f0592da..124862812 100644 --- a/packages/vrender-components/__tests__/browser/examples/mark-line.ts +++ b/packages/vrender-components/__tests__/browser/examples/mark-line.ts @@ -17,9 +17,11 @@ export function run() { const styleAttr = { endSymbol: { - symbolType: 'M0 0l-2 1 0.7289-1-0.7289-1z', + // symbolType: 'triangleLeft', + // symbolType: 'M0 0l-2 1 0.7289-1-0.7289-1z', size: 10, - refAngle: degreeToRadian(-90) + autoRotate: false + // refAngle: degreeToRadian(-90) }, label: { text: '平均值: 17.7', @@ -27,10 +29,7 @@ export function run() { autoRotate: guiObject.labelAutoRotate, refX: guiObject.labelRefX, refY: guiObject.labelRefY, - refAngle: degreeToRadian(guiObject.labelRefAngle), - formatMethod: text => { - return text + '格式化'; - } + refAngle: degreeToRadian(guiObject.labelRefAngle) } }; const markLine = new MarkLine({ diff --git a/packages/vrender-components/__tests__/browser/examples/mark-point.ts b/packages/vrender-components/__tests__/browser/examples/mark-point.ts index 3b177625e..0922e75ef 100644 --- a/packages/vrender-components/__tests__/browser/examples/mark-point.ts +++ b/packages/vrender-components/__tests__/browser/examples/mark-point.ts @@ -34,10 +34,7 @@ export function run() { refY: guiObject.itemRefY, refAngle: guiObject.itemRefAngle, textStyle: { - text: 'mark point label text', - formatMethod: text => { - return text + '格式化'; - } + text: 'mark point label text' }, richTextStyle: { textConfig: [ diff --git a/packages/vrender-components/package.json b/packages/vrender-components/package.json index 16303e65a..a9b78e066 100644 --- a/packages/vrender-components/package.json +++ b/packages/vrender-components/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender-components", - "version": "0.13.7", + "version": "0.13.8", "description": "components library for dp visualization", "sideEffects": false, "main": "cjs/index.js", @@ -24,7 +24,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@visactor/vrender": "workspace:0.13.6", + "@visactor/vrender": "workspace:0.13.7", "@visactor/vutils": "~0.13.0", "@visactor/vscale": "~0.13.0", "inversify": "6.0.1" diff --git a/packages/vrender-components/src/label/base.ts b/packages/vrender-components/src/label/base.ts index 6d1b8e901..6586bc749 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,13 @@ 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; } } @@ -281,21 +304,37 @@ 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 }); + // 尝试向内挤压 + 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); } if (isFunction(this.onAfterLabelOverlap)) { @@ -340,7 +379,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 +388,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 +455,7 @@ export abstract class LabelBase extends AbstractCompon } }); - this._textMap = currentTextMap; + this._graphicToText = currentTextMap; } protected _afterRelatedGraphicAttributeUpdate( @@ -486,20 +522,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 +542,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; diff --git a/packages/vrender-components/src/marker/type.ts b/packages/vrender-components/src/marker/type.ts index e3097c8c7..f8473e81c 100644 --- a/packages/vrender-components/src/marker/type.ts +++ b/packages/vrender-components/src/marker/type.ts @@ -64,10 +64,6 @@ export interface IMarkLabel extends Omit { * 标签的背景面板配置 */ panel?: IMarkBackgroundAttributes; - /** - * 标签自定义格式 - */ - formatMethod?: (text: string | number | string[] | number[]) => string; } export interface IMarkRef { diff --git a/packages/vrender-components/src/segment/segment.ts b/packages/vrender-components/src/segment/segment.ts index d6978a218..811f98c84 100644 --- a/packages/vrender-components/src/segment/segment.ts +++ b/packages/vrender-components/src/segment/segment.ts @@ -25,6 +25,7 @@ export class Segment extends AbstractComponent> { }, startSymbol: { visible: false, + autoRotate: true, symbolType: 'triangle', size: 12, refX: 0, @@ -37,6 +38,7 @@ export class Segment extends AbstractComponent> { }, endSymbol: { visible: false, + autoRotate: true, symbolType: 'triangle', size: 12, refX: 0, @@ -125,6 +127,7 @@ export class Segment extends AbstractComponent> { } private renderSymbol(attribute: SymbolAttributes, dim: string): ISymbol | undefined { + const { autoRotate = true } = attribute; let symbol; if (attribute?.visible) { const startAngle = this._startAngle; @@ -153,7 +156,7 @@ export class Segment extends AbstractComponent> { ...position, symbolType: symbolType as string, size, - angle: rotate + refAngle, + angle: autoRotate ? rotate + refAngle : 0, strokeBoundsBuffer: 0, ...style }); diff --git a/packages/vrender-components/src/segment/type.ts b/packages/vrender-components/src/segment/type.ts index 3d561e60f..296a87901 100644 --- a/packages/vrender-components/src/segment/type.ts +++ b/packages/vrender-components/src/segment/type.ts @@ -18,6 +18,10 @@ export type SymbolAttributes = { * symbol 大小 */ size?: number; + /** + * 自动旋转,沿着线的方向,默认 true + */ + autoRotate?: boolean; /** * symbol 相对line平行方向上的偏移 */ diff --git a/packages/vrender-components/src/tag/tag.ts b/packages/vrender-components/src/tag/tag.ts index 77402cb13..cf569dcbd 100644 --- a/packages/vrender-components/src/tag/tag.ts +++ b/packages/vrender-components/src/tag/tag.ts @@ -42,8 +42,7 @@ export class Tag extends AbstractComponent> { maxWidth, padding = 4, visible, - state, - formatMethod + state } = this.attribute as TagAttributes; const parsedPadding = normalizePadding(padding); @@ -85,7 +84,7 @@ export class Tag extends AbstractComponent> { textX += symbolPlaceWidth; const textAttrs = { - text: formatMethod ? formatMethod(text) : text, + text, visible: isValid(text) && visible !== false, lineHeight: textStyle?.fontSize, ...textStyle, diff --git a/packages/vrender-components/src/tag/type.ts b/packages/vrender-components/src/tag/type.ts index 125c2b2f4..dca3adf36 100644 --- a/packages/vrender-components/src/tag/type.ts +++ b/packages/vrender-components/src/tag/type.ts @@ -54,7 +54,6 @@ export type TagAttributes = { maxWidth?: number; visible?: boolean; state?: StateStyle; - formatMethod?: (text: string | number | string[] | number[]) => string; } & Omit; export type TagShapeAttributes = { diff --git a/packages/vrender-kits/CHANGELOG.json b/packages/vrender-kits/CHANGELOG.json index c9e229dc9..efa77ffa5 100644 --- a/packages/vrender-kits/CHANGELOG.json +++ b/packages/vrender-kits/CHANGELOG.json @@ -1,6 +1,18 @@ { "name": "@visactor/vrender-kits", "entries": [ + { + "version": "0.12.11", + "tag": "@visactor/vrender-kits_v0.12.11", + "date": "Thu, 03 Aug 2023 10:04:34 GMT", + "comments": { + "dependency": [ + { + "comment": "Updating dependency \"@visactor/vrender\" from `0.13.6` to `0.13.7`" + } + ] + } + }, { "version": "0.12.10", "tag": "@visactor/vrender-kits_v0.12.10", diff --git a/packages/vrender-kits/CHANGELOG.md b/packages/vrender-kits/CHANGELOG.md index e35a548fe..6f4cb9c2e 100644 --- a/packages/vrender-kits/CHANGELOG.md +++ b/packages/vrender-kits/CHANGELOG.md @@ -1,6 +1,11 @@ # Change Log - @visactor/vrender-kits -This log was last generated on Wed, 02 Aug 2023 03:13:00 GMT and should not be manually modified. +This log was last generated on Thu, 03 Aug 2023 10:04:34 GMT and should not be manually modified. + +## 0.12.11 +Thu, 03 Aug 2023 10:04:34 GMT + +_Version update only_ ## 0.12.10 Wed, 02 Aug 2023 03:13:00 GMT diff --git a/packages/vrender-kits/package.json b/packages/vrender-kits/package.json index 572633009..b8ed38feb 100644 --- a/packages/vrender-kits/package.json +++ b/packages/vrender-kits/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender-kits", - "version": "0.12.10", + "version": "0.12.11", "description": "", "sideEffects": false, "main": "cjs/index.js", @@ -21,7 +21,7 @@ "prepublishOnly": "npm run build" }, "dependencies": { - "@visactor/vrender": "workspace:0.13.6", + "@visactor/vrender": "workspace:0.13.7", "@visactor/vutils": "~0.13.0", "@resvg/resvg-js": "2.4.1", "canvas": "2.11.2", diff --git a/packages/vrender/CHANGELOG.json b/packages/vrender/CHANGELOG.json index 5b2d28219..dfdacda0e 100644 --- a/packages/vrender/CHANGELOG.json +++ b/packages/vrender/CHANGELOG.json @@ -1,6 +1,18 @@ { "name": "@visactor/vrender", "entries": [ + { + "version": "0.13.7", + "tag": "@visactor/vrender_v0.13.7", + "date": "Thu, 03 Aug 2023 10:04:34 GMT", + "comments": { + "patch": [ + { + "comment": "fix(marker): segement support autoRotate setting" + } + ] + } + }, { "version": "0.13.6", "tag": "@visactor/vrender_v0.13.6", diff --git a/packages/vrender/CHANGELOG.md b/packages/vrender/CHANGELOG.md index abd14476f..da8a42dc0 100644 --- a/packages/vrender/CHANGELOG.md +++ b/packages/vrender/CHANGELOG.md @@ -1,6 +1,13 @@ # Change Log - @visactor/vrender -This log was last generated on Wed, 02 Aug 2023 03:13:00 GMT and should not be manually modified. +This log was last generated on Thu, 03 Aug 2023 10:04:34 GMT and should not be manually modified. + +## 0.13.7 +Thu, 03 Aug 2023 10:04:34 GMT + +### Patches + +- fix(marker): segement support autoRotate setting ## 0.13.6 Wed, 02 Aug 2023 03:13:00 GMT diff --git a/packages/vrender/package.json b/packages/vrender/package.json index 3a557317d..25b34fee6 100644 --- a/packages/vrender/package.json +++ b/packages/vrender/package.json @@ -1,6 +1,6 @@ { "name": "@visactor/vrender", - "version": "0.13.6", + "version": "0.13.7", "description": "", "sideEffects": true, "main": "cjs/index.js", diff --git a/packages/vrender/src/graphic/node-tree.ts b/packages/vrender/src/graphic/node-tree.ts index a95e3fad9..91643b51f 100644 --- a/packages/vrender/src/graphic/node-tree.ts +++ b/packages/vrender/src/graphic/node-tree.ts @@ -177,7 +177,10 @@ export class Node extends EventEmitter implements INode { * @param newNode 要插入的节点 * @param referenceNode 插入到referenceNode之前 */ - insertBefore(newNode: INode, referenceNode: INode): INode | null { + insertBefore(newNode: INode, referenceNode: INode | undefined): INode | null { + if (!referenceNode) { + return this.appendChild(newNode); + } if (this._uid === newNode._uid) { return null; } @@ -218,7 +221,10 @@ export class Node extends EventEmitter implements INode { * @param newNode 要插入的节点 * @param referenceNode 插入到referenceNode之后 */ - insertAfter(newNode: INode, referenceNode: INode): INode | null { + insertAfter(newNode: INode, referenceNode: INode | undefined): INode | null { + if (!referenceNode) { + return this.appendChild(newNode); + } if (this._uid === newNode._uid) { return null; }