diff --git a/common/changes/@visactor/vchart/refactor-wordcloud-shape-tap-member-callback_2026-09-14.json b/common/changes/@visactor/vchart/refactor-wordcloud-shape-tap-member-callback_2026-09-14.json new file mode 100644 index 0000000000..14eb5ab87d --- /dev/null +++ b/common/changes/@visactor/vchart/refactor-wordcloud-shape-tap-member-callback_2026-09-14.json @@ -0,0 +1,11 @@ +{ + "changes": [ + { + "packageName": "@visactor/vchart", + "comment": "refactor: keep the word cloud shape afterRender tap as a stable member callback", + "type": "none" + } + ], + "packageName": "@visactor/vchart", + "email": "chendaxin.tk@bytedance.com" +} diff --git a/packages/vchart/__tests__/unit/series/word-cloud-shape-release.test.ts b/packages/vchart/__tests__/unit/series/word-cloud-shape-release.test.ts index 0f0e9f9355..b9ba372a58 100644 --- a/packages/vchart/__tests__/unit/series/word-cloud-shape-release.test.ts +++ b/packages/vchart/__tests__/unit/series/word-cloud-shape-release.test.ts @@ -1,7 +1,7 @@ import { VChart } from '../../../src/vchart-all'; import { createDiv, removeDom } from '../../util/dom'; -type Tap = { name: string; fn: () => void }; +type Tap = { name: string; fn: (stage: unknown) => void }; const SVG_MASK = ''; @@ -67,7 +67,7 @@ describe('word cloud shape afterRender tap', () => { expect(getWordCloudTaps().length).toBe(0); // series 已经 release,_option 为空,这个 tap 即使被别处留住也不能再抛错 - expect(() => tap.fn()).not.toThrow(); + expect(() => tap.fn(vchart.getStage())).not.toThrow(); }); it('should not be registered twice when the layout finishes more than once', () => { diff --git a/packages/vchart/src/series/word-cloud/base.ts b/packages/vchart/src/series/word-cloud/base.ts index 31a5591569..e43759f61e 100644 --- a/packages/vchart/src/series/word-cloud/base.ts +++ b/packages/vchart/src/series/word-cloud/base.ts @@ -46,6 +46,8 @@ import { wordCloud } from '../../theme/builtin/common/series/word-cloud'; import { LayoutZIndex } from '../../constant/layout'; import { ChartEvent } from '../../core'; +const AFTER_WORDCLOUD_SHAPE_DRAW_TAP = 'afterWordcloudShapeDraw'; + export type IBaseWordCloudSeriesSpec = Omit & { type: string }; export class BaseWordCloudSeries extends BaseSeries { @@ -79,8 +81,18 @@ export class BaseWordCloudSeries void }; + /** 形状词云布局完成后挂到 stage 上的 afterRender tap;引用稳定,便于 unTap 精确摘掉自己 */ + protected _afterWordcloudShapeDraw = (stage: IStage) => { + stage.hooks.afterRender.unTap(AFTER_WORDCLOUD_SHAPE_DRAW_TAP, this._afterWordcloudShapeDraw); + + // 布局是异步的,跑完之前 series 可能已经被 release,此时 _option 已置空 + const globalInstance = this._option?.globalInstance; + if (!globalInstance) { + return; + } + this._option.dispatchEvent?.(ChartEvent.afterWordcloudShapeDraw, { instance: globalInstance }); + globalInstance.getChart().getOption().performanceHook?.afterWordcloudShapeDraw?.(globalInstance); + }; protected _wordCloudConfig?: WordCloudConfigType; protected _wordCloudShapeConfig?: WordCloudShapeConfigType; @@ -397,29 +409,13 @@ export class BaseWordCloudSeries { - // 布局是异步的,跑完时 series 可能已经被 release,此时 _option 已置空 const stage = this._option?.globalInstance?.getStage(); if (!stage) { return; } - this._removeAfterWordcloudShapeDrawTap(); - - const afterWordcloudShapeDraw = () => { - // 需要等到真正渲染完成 - this._removeAfterWordcloudShapeDrawTap(); - - const globalInstance = this._option?.globalInstance; - if (!globalInstance) { - return; - } - this._option.dispatchEvent?.(ChartEvent.afterWordcloudShapeDraw, { - instance: globalInstance - }); - globalInstance.getChart().getOption().performanceHook?.afterWordcloudShapeDraw?.(globalInstance); - }; - - this._afterWordcloudShapeDrawTap = { stage, fn: afterWordcloudShapeDraw }; - stage.hooks.afterRender.tap('afterWordcloudShapeDraw', afterWordcloudShapeDraw); + // 同一个 series 再次布局时,先摘掉上一轮还没被触发的那个 + stage.hooks.afterRender.unTap(AFTER_WORDCLOUD_SHAPE_DRAW_TAP, this._afterWordcloudShapeDraw); + stage.hooks.afterRender.tap(AFTER_WORDCLOUD_SHAPE_DRAW_TAP, this._afterWordcloudShapeDraw); }, dataIndexKey: DEFAULT_DATA_KEY, text: wordSpec.formatMethod @@ -559,19 +555,12 @@ export class BaseWordCloudSeries