Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions common/changes/@visactor/vchart/fix-map-bug_2026-04-20-08-30.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vchart",
"comment": "fix: vchart relayout api not work bug. fix#4537",
"type": "none"
}
],
"packageName": "@visactor/vchart"
}
10 changes: 10 additions & 0 deletions common/changes/@visactor/vchart/fix-map-bug_2026-04-20-08-58.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"changes": [
{
"packageName": "@visactor/vchart",
"comment": "fix: label not follow when drag. fix#4547",
"type": "none"
}
],
"packageName": "@visactor/vchart"
}
40 changes: 40 additions & 0 deletions packages/vchart/__tests__/unit/core/vchart.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down
13 changes: 9 additions & 4 deletions packages/vchart/src/core/vchart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

/**
Expand Down
40 changes: 6 additions & 34 deletions packages/vchart/src/series/map/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,24 +252,10 @@ export class MapSeries<T extends IMapSeriesSpec = IMapSeriesSpec> 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();
}
}

Expand All @@ -287,24 +273,10 @@ export class MapSeries<T extends IMapSeriesSpec = IMapSeriesSpec> 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();
}
}

Expand Down
98 changes: 65 additions & 33 deletions packages/vchart/src/series/scatter/scatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends IScatterSeriesSpec = IScatterSeriesSpec> extends CartesianSeries<T> {
static readonly type: string = SeriesTypeEnum.scatter;
Expand Down Expand Up @@ -350,51 +351,82 @@ export class ScatterSeries<T extends IScatterSeriesSpec = IScatterSeriesSpec> 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() {
Expand Down
Loading