diff --git a/common/changes/@visactor/vchart/fix-map-bug_2026-04-20-08-30.json b/common/changes/@visactor/vchart/fix-map-bug_2026-04-20-08-30.json new file mode 100644 index 0000000000..f74780a959 --- /dev/null +++ b/common/changes/@visactor/vchart/fix-map-bug_2026-04-20-08-30.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vchart", + "comment": "fix: vchart relayout api not work bug. fix#4537", + "type": "none" + } + ], + "packageName": "@visactor/vchart" +} \ No newline at end of file diff --git a/common/changes/@visactor/vchart/fix-map-bug_2026-04-20-08-58.json b/common/changes/@visactor/vchart/fix-map-bug_2026-04-20-08-58.json new file mode 100644 index 0000000000..e15c40e64c --- /dev/null +++ b/common/changes/@visactor/vchart/fix-map-bug_2026-04-20-08-58.json @@ -0,0 +1,10 @@ +{ + "changes": [ + { + "packageName": "@visactor/vchart", + "comment": "fix: label not follow when drag. fix#4547", + "type": "none" + } + ], + "packageName": "@visactor/vchart" +} \ No newline at end of file diff --git a/packages/vchart/__tests__/unit/core/vchart.test.ts b/packages/vchart/__tests__/unit/core/vchart.test.ts index 7f0f2b05c7..c31a5dbdc1 100644 --- a/packages/vchart/__tests__/unit/core/vchart.test.ts +++ b/packages/vchart/__tests__/unit/core/vchart.test.ts @@ -299,6 +299,46 @@ describe('VChart', () => { expect((labels.children[3] as Text).attribute.fillOpacity).toBe(1); expect((labels.children[3] as Text).attribute.text).toBe(1200); }); + + it('reLayout', () => { + const spec: IBarChartSpec = { + type: 'bar', + width: 200, + height: 150, + data: [ + { + id: 'data', + values: [ + { x: 'Mon', y: 100 }, + { x: 'Tue', y: 66 } + ] + } + ], + xField: 'x', + yField: 'y', + axes: [{ orient: 'bottom', type: 'band' }, { orient: 'left' }] + }; + + vchart = new VChart(spec, { + renderCanvas: canvasDom, + animation: false + }); + vchart.renderSync(); + + const chart = vchart.getChart(); + const compiler = vchart.getCompiler(); + const resetLayoutItemTagSpy = jest.spyOn(chart, 'resetLayoutItemTag'); + const setLayoutTagSpy = jest.spyOn(chart, 'setLayoutTag'); + const renderSpy = jest.spyOn(compiler, 'render'); + const onEvaluateEndSpy = jest.spyOn(chart, 'onEvaluateEnd'); + + vchart.reLayout(); + + expect(resetLayoutItemTagSpy).toHaveBeenCalledTimes(1); + expect(setLayoutTagSpy).toHaveBeenCalledWith(true, null, false); + expect(renderSpy).toHaveBeenCalledTimes(1); + expect(onEvaluateEndSpy).toHaveBeenCalledTimes(1); + }); }); describe('convertDatumToPosition and convertValueToPosition', () => { diff --git a/packages/vchart/src/core/vchart.ts b/packages/vchart/src/core/vchart.ts index ec12b23cac..7471f449fc 100644 --- a/packages/vchart/src/core/vchart.ts +++ b/packages/vchart/src/core/vchart.ts @@ -1815,10 +1815,15 @@ export class VChart implements IVChart { * 强制重新布局 */ reLayout() { - this._chart?.resetLayoutItemTag(); - // Force immediate layout + render to ensure geo components reset roam state. - this._chart?.setLayoutTag(true, null, false); - this._compiler?.render(); + if (!this._chart || !this._compiler || this._isReleased) { + return; + } + this._chart.resetLayoutItemTag(); + this._chart.setLayoutTag(true, null, false); + this._compiler.render(); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + this._chart.onEvaluateEnd(); } /** diff --git a/packages/vchart/src/series/map/map.ts b/packages/vchart/src/series/map/map.ts index 4b2116d444..6fbc11206f 100644 --- a/packages/vchart/src/series/map/map.ts +++ b/packages/vchart/src/series/map/map.ts @@ -252,24 +252,10 @@ export class MapSeries extends GeoSer } pathGroup.scale(scale, scale, scaleCenter); } - const labelComponent = this._labelMark?.getComponent(); + const vgrammarLabel = this._labelMark?.getComponent(); - if (labelComponent?.renderInner) { - labelComponent.renderInner(); - } - const vgrammarLabel = labelComponent?.getComponent?.(); - if (vgrammarLabel?.evaluate) { - (vgrammarLabel as any).evaluate(null, null); - } - // 标签跟随地图 - const labelGroup = labelComponent?.getProduct?.(); - if (labelGroup && scale && scaleCenter) { - if (!labelGroup.attribute?.postMatrix) { - labelGroup.setAttributes({ - postMatrix: new Matrix() - }); - } - labelGroup.scale(scale, scale, scaleCenter); + if (vgrammarLabel) { + vgrammarLabel.renderInner(); } } @@ -287,24 +273,10 @@ export class MapSeries extends GeoSer } pathGroup.translate(delta[0], delta[1]); } - const labelComponent = this._labelMark?.getComponent(); + const vgrammarLabel = this._labelMark?.getComponent(); - if (labelComponent?.renderInner) { - labelComponent.renderInner(); - } - const vgrammarLabel = labelComponent?.getComponent?.(); - if (vgrammarLabel?.evaluate) { - (vgrammarLabel as any).evaluate(null, null); - } - // 标签跟随地图 - const labelGroup = labelComponent?.getProduct?.(); - if (labelGroup && delta) { - if (!labelGroup.attribute?.postMatrix) { - labelGroup.setAttributes({ - postMatrix: new Matrix() - }); - } - labelGroup.translate(delta[0], delta[1]); + if (vgrammarLabel) { + vgrammarLabel.renderInner(); } } diff --git a/packages/vchart/src/series/scatter/scatter.ts b/packages/vchart/src/series/scatter/scatter.ts index 9f41789512..7ac4988754 100644 --- a/packages/vchart/src/series/scatter/scatter.ts +++ b/packages/vchart/src/series/scatter/scatter.ts @@ -28,6 +28,7 @@ import { ScatterSeriesSpecTransformer } from './scatter-transformer'; import { getGroupAnimationParams } from '../util/utils'; import { registerCartesianLinearAxis, registerCartesianBandAxis } from '../../component/axis/cartesian'; import { scatter } from '../../theme/builtin/common/series/scatter'; +import type { IGraphic } from '@visactor/vrender-core'; export class ScatterSeries extends CartesianSeries { static readonly type: string = SeriesTypeEnum.scatter; @@ -350,51 +351,82 @@ export class ScatterSeries ex /** * 处理缩放 */ - handleZoom(e: any) { - this.getMarksWithoutRoot().forEach(mark => { - if (!mark) { - return; - } - const graphics = mark.getGraphics(); + private _updateSymbolGraphicPosition() { + const graphics = this._symbolMark?.getGraphics(); - if (!graphics || !graphics.length) { - return; - } + if (!graphics || !graphics.length) { + return; + } - graphics.forEach((graphicItem: IMarkGraphic, i: number) => { - const datum = graphicItem?.context?.data?.[0]; - const newPosition = this.dataToPosition(datum); - if (newPosition && graphicItem) { - graphicItem.translateTo(newPosition.x, newPosition.y); - } - }); + graphics.forEach((graphicItem: IMarkGraphic) => { + const datum = graphicItem?.context?.data?.[0]; + const newPosition = this.dataToPosition(datum); + if (newPosition && graphicItem) { + (graphicItem as unknown as IGraphic).setAttributes({ + x: newPosition.x, + y: newPosition.y + }); + } }); + } - const labelComponent = this._labelMark?.getComponent(); + private _ensureLabelGraphicPostMatrix() { + const labelGraphic = this._labelMark?.getComponent()?.getComponent(); - if (labelComponent?.renderInner) { - labelComponent.renderInner(); + if (labelGraphic && !labelGraphic.attribute.postMatrix) { + labelGraphic.setAttributes({ + postMatrix: new Matrix() + }); } - const vgrammarLabel = labelComponent?.getComponent?.(); - if (vgrammarLabel?.evaluate) { - (vgrammarLabel as any).evaluate(null, null); + + return labelGraphic; + } + + handleZoom(e: any) { + const { scale, scaleCenter } = e; + if (scale === 1) { + return; } - // 标签跟随地图 - const labelGroup = labelComponent?.getProduct?.(); - if (labelGroup && e?.scale && e?.scaleCenter) { - if (!labelGroup.attribute?.postMatrix) { - labelGroup.setAttributes({ - postMatrix: new Matrix() - }); - } - labelGroup.scale(e.scale, e.scale, e.scaleCenter); + this._updateSymbolGraphicPosition(); + + const labelGraphic = this._ensureLabelGraphicPostMatrix(); + + if (labelGraphic) { + labelGraphic.scale(scale, scale, scaleCenter); } } handlePan(e: any) { - // TODO 现在处理好像一模一样 - this.handleZoom(e); + const { delta } = e; + if (delta?.[0] === 0 && delta?.[1] === 0) { + return; + } + + this._updateSymbolGraphicPosition(); + + const labelGraphic = this._ensureLabelGraphicPostMatrix(); + + if (labelGraphic) { + labelGraphic.translate(delta[0], delta[1]); + } + } + + onLayoutEnd(): void { + super.onLayoutEnd(); + this._updateSymbolGraphicPosition(); + + const labelGraphic = this._labelMark?.getComponent()?.getComponent(); + if (labelGraphic?.attribute.postMatrix) { + labelGraphic.setAttributes({ + postMatrix: new Matrix() + }); + } + + const vgrammarLabel = this._labelMark?.getComponent(); + if (vgrammarLabel) { + vgrammarLabel.renderInner(); + } } getDefaultShapeType() {