From 6f66026cf9cb70794e95dff7d66473111a85ae54 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Mon, 27 Apr 2026 14:06:54 +0800 Subject: [PATCH 1/7] chore: unblock VChart validation on alpha.2 Use the published VRender alpha.2 packages for the VChart validation branch. Keep the minimal app-aware stage path and external ownership fixes in review scope. Add regression coverage for VRender patches and final attrs. Constraint: Published npm alpha is the validation source Constraint: option.stage and option.app remain external-owned Rejected: Local linked VRender build | final evidence must use npm alpha Rejected: Broad env/bootstrap migration | this branch keeps stage changes minimal Confidence: high Scope-risk: moderate Directive: Do not downgrade @visactor/vrender* or bypass VRender state patches Tested: rush install --check-only Tested: rush test --only @visactor/vchart Tested: rush build --to @visactor/vchart Tested: eslint touched VChart files Tested: browser strict smoke for basicBar/basicLine/textHeavy hover/select Not-tested: full miniApp/wx/tt/harmony/lynx app factory migration --- common/config/rush/pnpm-lock.yaml | 116 ++++----- docs/package.json | 6 +- packages/openinula-vchart/package.json | 6 +- packages/react-vchart/package.json | 4 +- packages/vchart-extension/package.json | 8 +- .../__tests__/unit/core/vchart-event.test.ts | 91 +++++++ .../vchart/__tests__/unit/core/vchart.test.ts | 241 +++++++++++++++++- packages/vchart/package.json | 8 +- packages/vchart/src/compile/compiler.ts | 36 ++- .../vchart/src/compile/interface/compiler.ts | 10 +- packages/vchart/src/compile/stage-app.ts | 51 ++++ tools/story-player/package.json | 8 +- 12 files changed, 494 insertions(+), 91 deletions(-) create mode 100644 packages/vchart/src/compile/stage-app.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 3c7d870779..546b5efda2 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -37,11 +37,11 @@ importers: specifier: 1.2.4-alpha.5 version: 1.2.4-alpha.5 '@visactor/vrender': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-kits': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vtable': specifier: 1.19.0-alpha.0 version: 1.19.0-alpha.0 @@ -203,11 +203,11 @@ importers: specifier: workspace:2.0.22 version: link:../vchart '@visactor/vrender-core': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-kits': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -294,11 +294,11 @@ importers: specifier: workspace:2.0.22 version: link:../vchart-extension '@visactor/vrender-core': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-kits': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -529,17 +529,17 @@ importers: specifier: ~1.0.23 version: 1.0.23 '@visactor/vrender-animate': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-components': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-core': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-kits': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vscale': specifier: ~1.0.23 version: 1.0.23 @@ -692,17 +692,17 @@ importers: specifier: ~1.0.23 version: 1.0.23 '@visactor/vrender-animate': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-components': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-core': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-kits': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -1260,14 +1260,14 @@ importers: specifier: workspace:2.0.22 version: link:../../packages/vchart '@visactor/vrender': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-core': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-kits': - specifier: 1.0.45 - version: 1.0.45 + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -3073,29 +3073,29 @@ packages: '@visactor/vrender-animate@1.0.0-alpha.18': resolution: {integrity: sha512-9kTtvp1ef+1t+AtUiza6A7qBQP7SmvOu3/ILGrqs/HGdZVj1XGjbYvD/X/zwKJ3LEb7gGV5fa8x95e4czTvRSA==} - '@visactor/vrender-animate@1.0.45': - resolution: {integrity: sha512-6v7LRpr+zugxR8JH3RCnodYxrrzHkSp4GaBTNwXuJ7bwThi/YzXTvmBz2pfQNmUG/rRze8Bm7vqneHVdXuFhng==} + '@visactor/vrender-animate@1.1.0-alpha.2': + resolution: {integrity: sha512-jlegJK92jVvT/PF16nMeRjlN68IoSgvJz/tKgsZVveJCt5276LSm2K5vpIjXNeaEKvmUnKes+45sZZX6kWNC/A==} '@visactor/vrender-components@1.0.0-alpha.18': resolution: {integrity: sha512-7Euq+ZfswL74n2pgkaqZSsPxoSa5SPIGyXatN1eUrdzM2Z0kX6U0RcJg01fctvRs4op6WhcecRLqGvnHcBeb9Q==} - '@visactor/vrender-components@1.0.45': - resolution: {integrity: sha512-rYsG/rncT5FgYYWqv09f6LkZEAk/IS45IEYWBD0SCgKguYnpEebYDmDba1muJGn+rRv/vGlqg1pYqXLw8mEU5w==} + '@visactor/vrender-components@1.1.0-alpha.2': + resolution: {integrity: sha512-nmBTBXEPvDRYbvXtOCLZ89+9WK1A+wLsE4RbuKv2omjA9D+XYRE7QZsW3O1g1/XBD8JqDVDiz+tk2Pp11IBBMw==} '@visactor/vrender-core@1.0.0-alpha.18': resolution: {integrity: sha512-0ihtNvCyNkOsWPFgRqowHzq0IcQgS2Wl/nPpKbVtxWKveenwlhA+ZKoQvam6VJyBY7jeNe1pROy0mJMDyVAJQw==} - '@visactor/vrender-core@1.0.45': - resolution: {integrity: sha512-kvYAsKGZ+dXbhOQzjjbMkRzI815KgaHoWOW7iyVorXTldQBTsxLtFIi/J3VfYqGdM3apUgsFoy8uvjS5DepPUA==} + '@visactor/vrender-core@1.1.0-alpha.2': + resolution: {integrity: sha512-f9BTOK3Rv5yiTxMJMPLV2VYIgwcsakJ/VXCOp88hbWgy/or2pvPW4/42sOLALDMCjSnpd2zGhV2R2vy2+nSD2w==} '@visactor/vrender-kits@1.0.0-alpha.18': resolution: {integrity: sha512-Tvolkq+4G8qiPFZo0Aj8M//Yr6jR2h8FNkFEyWM9gbQbEiTkjpmHAJOYnoSsaPtPrcMSlG4EhJSFDk6ymANHVg==} - '@visactor/vrender-kits@1.0.45': - resolution: {integrity: sha512-iaeRitht8IqNvJwdKmcPKAYL0a4+f8xhK2bWUumSQjNbZJQx9UzGuDp73cIdNXzCNoCiJZElpsOiIGu3kymqiw==} + '@visactor/vrender-kits@1.1.0-alpha.2': + resolution: {integrity: sha512-fXG3SB/PDcmA2oK6FqHTBvb8438BDeeCkpIvzt1WI/lpghzD6wT7Dd3HhY8hxonArDUUqAsorZyFeQFNPezUTQ==} - '@visactor/vrender@1.0.45': - resolution: {integrity: sha512-mHIB9euxbgpotNLe8cn8IThTJbXgrn/B1Nx/PCqp/OeBEcNihmy5X9ODJVgUhrF3dY1ZX0EeE2zrEE0mE/+eHQ==} + '@visactor/vrender@1.1.0-alpha.2': + resolution: {integrity: sha512-MNW2rTJLgCVa0T4TTavQ+fMJp2PkvkZwxWAwdzdNlFAIX9WriF24L+Xqwu8nvqsJiUz2BEUbgYP+7sTZcn7krA==} '@visactor/vscale@0.18.18': resolution: {integrity: sha512-iRG4kv+5Fv4KX3AxEfV95XU3I6OmF0QizyAhqHxKa7L1MaT+MRvDDk5zHWf1E8gialLbL2xDe3GnT6g/4u5jhA==} @@ -15026,9 +15026,9 @@ snapshots: '@visactor/vrender-core': 1.0.0-alpha.18 '@visactor/vutils': 1.0.4 - '@visactor/vrender-animate@1.0.45': + '@visactor/vrender-animate@1.1.0-alpha.2': dependencies: - '@visactor/vrender-core': 1.0.45 + '@visactor/vrender-core': 1.1.0-alpha.2 '@visactor/vutils': 1.0.23 '@visactor/vrender-components@1.0.0-alpha.18': @@ -15039,11 +15039,11 @@ snapshots: '@visactor/vscale': 1.0.4 '@visactor/vutils': 1.0.4 - '@visactor/vrender-components@1.0.45': + '@visactor/vrender-components@1.1.0-alpha.2': dependencies: - '@visactor/vrender-animate': 1.0.45 - '@visactor/vrender-core': 1.0.45 - '@visactor/vrender-kits': 1.0.45 + '@visactor/vrender-animate': 1.1.0-alpha.2 + '@visactor/vrender-core': 1.1.0-alpha.2 + '@visactor/vrender-kits': 1.1.0-alpha.2 '@visactor/vscale': 1.0.23 '@visactor/vutils': 1.0.23 @@ -15052,7 +15052,7 @@ snapshots: '@visactor/vutils': 1.0.4 color-convert: 2.0.1 - '@visactor/vrender-core@1.0.45': + '@visactor/vrender-core@1.1.0-alpha.2': dependencies: '@visactor/vutils': 1.0.23 color-convert: 2.0.1 @@ -15066,21 +15066,21 @@ snapshots: lottie-web: 5.13.0 roughjs: 4.5.2 - '@visactor/vrender-kits@1.0.45': + '@visactor/vrender-kits@1.1.0-alpha.2': dependencies: '@resvg/resvg-js': 2.4.1 - '@visactor/vrender-core': 1.0.45 + '@visactor/vrender-core': 1.1.0-alpha.2 '@visactor/vutils': 1.0.23 gifuct-js: 2.1.2 lottie-web: 5.13.0 roughjs: 4.6.6 - '@visactor/vrender@1.0.45': + '@visactor/vrender@1.1.0-alpha.2': dependencies: - '@visactor/vrender-animate': 1.0.45 - '@visactor/vrender-components': 1.0.45 - '@visactor/vrender-core': 1.0.45 - '@visactor/vrender-kits': 1.0.45 + '@visactor/vrender-animate': 1.1.0-alpha.2 + '@visactor/vrender-components': 1.1.0-alpha.2 + '@visactor/vrender-core': 1.1.0-alpha.2 + '@visactor/vrender-kits': 1.1.0-alpha.2 '@visactor/vscale@0.18.18': dependencies: diff --git a/docs/package.json b/docs/package.json index 5f958df927..916924de51 100644 --- a/docs/package.json +++ b/docs/package.json @@ -19,8 +19,8 @@ "@visactor/vchart-theme": "~1.6.6", "@visactor/vmind": "1.2.4-alpha.5", "@visactor/vutils": "~1.0.23", - "@visactor/vrender": "1.0.45", - "@visactor/vrender-kits": "1.0.45", + "@visactor/vrender": "1.1.0-alpha.2", + "@visactor/vrender-kits": "1.1.0-alpha.2", "@visactor/vtable": "1.19.0-alpha.0", "@visactor/vtable-editors": "1.19.0-alpha.0", "@visactor/vtable-gantt": "1.19.0-alpha.0", @@ -58,4 +58,4 @@ "react-device-detect": "^2.2.2", "minimist": "1.2.8" } -} \ No newline at end of file +} diff --git a/packages/openinula-vchart/package.json b/packages/openinula-vchart/package.json index 2f7629c7f8..bcc4a86dae 100644 --- a/packages/openinula-vchart/package.json +++ b/packages/openinula-vchart/package.json @@ -30,8 +30,8 @@ "dependencies": { "@visactor/vchart": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", - "@visactor/vrender-core": "1.0.45", - "@visactor/vrender-kits": "1.0.45", + "@visactor/vrender-core": "1.1.0-alpha.2", + "@visactor/vrender-kits": "1.1.0-alpha.2", "react-is": "^18.2.0" }, "devDependencies": { @@ -78,4 +78,4 @@ "access": "public", "registry": "https://registry.npmjs.org/" } -} \ No newline at end of file +} diff --git a/packages/react-vchart/package.json b/packages/react-vchart/package.json index 16de6da94c..3aa5be4964 100644 --- a/packages/react-vchart/package.json +++ b/packages/react-vchart/package.json @@ -36,8 +36,8 @@ "@visactor/vchart": "workspace:2.0.22", "@visactor/vchart-extension": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", - "@visactor/vrender-core": "1.0.45", - "@visactor/vrender-kits": "1.0.45", + "@visactor/vrender-core": "1.1.0-alpha.2", + "@visactor/vrender-kits": "1.1.0-alpha.2", "react-is": "^18.2.0" }, "devDependencies": { diff --git a/packages/vchart-extension/package.json b/packages/vchart-extension/package.json index cda702106a..25789045fa 100644 --- a/packages/vchart-extension/package.json +++ b/packages/vchart-extension/package.json @@ -26,10 +26,10 @@ "start": "ts-node __tests__/runtime/browser/scripts/initVite.ts && vite serve __tests__/runtime/browser" }, "dependencies": { - "@visactor/vrender-core": "1.0.45", - "@visactor/vrender-kits": "1.0.45", - "@visactor/vrender-components": "1.0.45", - "@visactor/vrender-animate": "1.0.45", + "@visactor/vrender-core": "1.1.0-alpha.2", + "@visactor/vrender-kits": "1.1.0-alpha.2", + "@visactor/vrender-components": "1.1.0-alpha.2", + "@visactor/vrender-animate": "1.1.0-alpha.2", "@visactor/vchart": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", "@visactor/vdataset": "~1.0.23", diff --git a/packages/vchart/__tests__/unit/core/vchart-event.test.ts b/packages/vchart/__tests__/unit/core/vchart-event.test.ts index 39ec0a0efe..2896595ed3 100644 --- a/packages/vchart/__tests__/unit/core/vchart-event.test.ts +++ b/packages/vchart/__tests__/unit/core/vchart-event.test.ts @@ -310,6 +310,97 @@ describe('vchart event test', () => { } }); + it('should apply interaction state style through vrender state patch', () => { + const stateContainer = createDiv(); + const stateDom = createDiv(stateContainer); + + const chartWithState = new VChart( + { + type: 'bar', + width: 400, + height: 300, + data: [ + { + id: 'data', + values: [ + { x: 'Mon', y: 10 }, + { x: 'Tue', y: 12 } + ] + } + ], + xField: 'x', + yField: 'y', + hover: { enable: true, trigger: 'pointerover', triggerOff: 'pointerout' }, + select: { enable: true, trigger: 'click', mode: 'single' }, + bar: { + state: { + hover: { + fillOpacity: 0.31 + }, + selected: { + stroke: '#111827', + lineWidth: 4 + } + } + } + } as IBarChartSpec, + { + dom: stateDom, + animation: false + } + ); + + chartWithState.renderSync(); + + try { + const chart = chartWithState.getChart() as IChart; + const barSeries = chart.getAllSeries()[0]; + const barMark = barSeries.getMarks().find((mark: IMark) => mark.name === 'bar'); + expect(barMark).toBeDefined(); + if (!barMark) { + throw new Error('Expected bar mark to exist'); + } + + const barGraphic = barMark.getGraphics()[0] as IMarkGraphic & { + attribute: Record; + resolvedStatePatch?: Record; + stateProxy?: (stateName: string, states: string[]) => Record; + }; + expect(barGraphic).toBeDefined(); + expect(typeof barGraphic.stateProxy).toBe('function'); + + const hoverPatch = barGraphic.stateProxy?.('hover', ['hover']); + expect(hoverPatch?.fillOpacity).toBe(0.31); + + chart.getEvent().emit('pointerover', { item: barGraphic } as unknown as BaseEventParams); + + expect(barGraphic.hasState('hover')).toBe(true); + expect(barGraphic.resolvedStatePatch?.fillOpacity).toBe(0.31); + expect(barGraphic.attribute.fillOpacity).toBe(0.31); + + chart.getEvent().emit('pointerout', { item: barGraphic } as unknown as BaseEventParams); + + expect(barGraphic.hasState('hover')).toBe(false); + expect(barGraphic.resolvedStatePatch).toBeUndefined(); + expect(barGraphic.attribute.fillOpacity).not.toBe(0.31); + + const selectedPatch = barGraphic.stateProxy?.('selected', ['selected']); + expect(selectedPatch?.stroke).toBe('#111827'); + expect(selectedPatch?.lineWidth).toBe(4); + + chart.getEvent().emit('click', { item: barGraphic } as unknown as BaseEventParams); + + expect(barGraphic.hasState('selected')).toBe(true); + expect(barGraphic.resolvedStatePatch?.stroke).toBe('#111827'); + expect(barGraphic.resolvedStatePatch?.lineWidth).toBe(4); + expect(barGraphic.attribute.stroke).toBe('#111827'); + expect(barGraphic.attribute.lineWidth).toBe(4); + } finally { + chartWithState.release(); + removeDom(stateContainer); + } + }); + it('should merge only identical interaction triggers in common chart', () => { const commonContainer = createDiv(); const commonDom = createDiv(commonContainer); diff --git a/packages/vchart/__tests__/unit/core/vchart.test.ts b/packages/vchart/__tests__/unit/core/vchart.test.ts index c31a5dbdc1..03a09b6b4e 100644 --- a/packages/vchart/__tests__/unit/core/vchart.test.ts +++ b/packages/vchart/__tests__/unit/core/vchart.test.ts @@ -1,6 +1,13 @@ -import type { Group, IArc, Text } from '@visactor/vrender-core'; +import { createBrowserApp, Stage, type Group, type IApp, type IArc, type Text } from '@visactor/vrender-core'; +import { + installBrowserEnvToApp, + installBrowserPickersToApp, + installDefaultGraphicsToApp +} from '@visactor/vrender-kits'; import type { IBarChartSpec } from '../../../src'; import { default as VChart } from '../../../src'; +import { getDefaultVRenderApp } from '../../../src/compile/stage-app'; +import { registerBrowserEnv } from '../../../src/env'; import { createDiv, createCanvas, removeDom } from '../../util/dom'; import type { ICommonChartSpec } from '../../../src/chart/common'; import type { IAreaSeriesSpec } from '../../../src/series/area/interface'; @@ -508,7 +515,7 @@ describe('VChart', () => { expect(value1).toBe(mark.attribute.x); const value2 = vchart.convertValueToPosition(0, { axisId: 'left' }); - expect(value2).toBe(394); + expect(value2).toBe(vchart.getChart()!.getAllSeries()[0].getRegion().getLayoutRect().height); }); it('should convert correctly in funnel chart', () => { @@ -759,4 +766,234 @@ describe('VChart', () => { expect(axis.getScale().domain()).toEqual([-52, 30]); }); }); + + describe('external stage ownership', () => { + let canvasDom: HTMLCanvasElement; + let charts: VChart[]; + let externalStage: Stage | undefined; + let rawExternalStageRelease: (() => void) | undefined; + let externalApp: IApp | undefined; + let rawExternalAppRelease: (() => void) | undefined; + + const spec = (valueOffset: number = 0): IBarChartSpec => ({ + type: 'bar', + width: 200, + height: 150, + data: [ + { + id: 'data', + values: [ + { x: 'A', y: 12 + valueOffset }, + { x: 'B', y: 18 + valueOffset } + ] + } + ], + xField: 'x', + yField: 'y', + animation: false + }); + + const createExternalStage = () => { + registerBrowserEnv(); + externalStage = new Stage({ + width: 200, + height: 150, + canvas: canvasDom, + autoRender: true, + disableDirtyBounds: true + }); + rawExternalStageRelease = externalStage.release.bind(externalStage); + return externalStage; + }; + + const createExternalApp = () => { + externalApp = createBrowserApp(); + installBrowserEnvToApp(externalApp); + installDefaultGraphicsToApp(externalApp); + installBrowserPickersToApp(externalApp); + rawExternalAppRelease = externalApp.release.bind(externalApp); + return externalApp; + }; + + beforeEach(() => { + canvasDom = createCanvas(); + canvasDom.width = 200; + canvasDom.height = 150; + charts = []; + }); + + afterEach(() => { + charts.forEach(chart => { + if (!(chart as any)._isReleased) { + chart.release(); + } + }); + if (externalStage && (externalStage as any).releaseStatus !== 'released') { + rawExternalStageRelease?.(); + } + if (externalApp && !externalApp.released) { + rawExternalAppRelease?.(); + } + removeDom(canvasDom); + externalStage = undefined; + rawExternalStageRelease = undefined; + externalApp = undefined; + rawExternalAppRelease = undefined; + }); + + it('should not release an externally owned stage and should remove chart-owned root group', () => { + const stage = createExternalStage(); + const releaseStage = jest.fn(() => rawExternalStageRelease?.()); + stage.release = releaseStage as any; + + const chart = new VChart(spec(), { + stage: stage as any, + animation: false + }); + charts.push(chart); + chart.renderSync(); + + const rootGroup = stage.defaultLayer.find(node => node.name === 'root', false); + expect(rootGroup).toBeDefined(); + + chart.release(); + + expect(releaseStage).not.toHaveBeenCalled(); + expect(stage.window).toBeDefined(); + expect(stage.defaultLayer).toBeDefined(); + expect(stage.defaultLayer.find(node => node === rootGroup, false)).toBeFalsy(); + }); + + it('should allow a second VChart instance to reuse the same external stage', () => { + const stage = createExternalStage(); + const releaseStage = jest.fn(() => rawExternalStageRelease?.()); + stage.release = releaseStage as any; + + const first = new VChart(spec(), { + stage: stage as any, + animation: false + }); + charts.push(first); + first.renderSync(); + first.updateSpecSync(spec(3)); + first.release(); + + expect(releaseStage).not.toHaveBeenCalled(); + + const second = new VChart(spec(6), { + stage: stage as any, + animation: false + }); + charts.push(second); + second.renderSync(); + second.release(); + + expect(releaseStage).not.toHaveBeenCalled(); + }); + + it('should still release an internally created stage', () => { + const chart = new VChart(spec(), { + renderCanvas: canvasDom, + animation: false + }); + charts.push(chart); + chart.renderSync(); + + const stage = chart.getStage() as unknown as Stage; + const rawRelease = stage.release.bind(stage); + const releaseStage = jest.fn(() => rawRelease()); + stage.release = releaseStage as any; + + chart.release(); + + expect(releaseStage).toHaveBeenCalledTimes(1); + }); + + it('should use an external app to create an internally owned stage without releasing the app', () => { + const app = createExternalApp(); + const createStage = jest.spyOn(app, 'createStage'); + const releaseApp = jest.fn(() => rawExternalAppRelease?.()); + app.release = releaseApp as any; + + const chart = new VChart(spec(), { + app, + renderCanvas: canvasDom, + animation: false + } as any); + charts.push(chart); + chart.renderSync(); + + expect(createStage).toHaveBeenCalledTimes(1); + + const stage = chart.getStage() as unknown as Stage; + const rawRelease = stage.release.bind(stage); + const releaseStage = jest.fn(() => rawRelease()); + stage.release = releaseStage as any; + + chart.release(); + + expect(releaseStage).toHaveBeenCalledTimes(1); + expect(releaseApp).not.toHaveBeenCalled(); + }); + + it('should reuse the fallback app while keeping internally created stages isolated', () => { + const fallbackApp = getDefaultVRenderApp('desktop-browser'); + const createStage = jest.spyOn(fallbackApp, 'createStage'); + const secondCanvasDom = createCanvas(); + secondCanvasDom.width = 200; + secondCanvasDom.height = 150; + + const first = new VChart(spec(), { + renderCanvas: canvasDom, + animation: false + }); + charts.push(first); + first.renderSync(); + + const second = new VChart(spec(6), { + renderCanvas: secondCanvasDom, + animation: false + }); + charts.push(second); + second.renderSync(); + + const firstStage = first.getStage() as unknown as Stage; + const secondStage = second.getStage() as unknown as Stage; + const rawSecondStageRelease = secondStage.release.bind(secondStage); + const releaseSecondStage = jest.fn(() => rawSecondStageRelease()); + secondStage.release = releaseSecondStage as any; + + expect(getDefaultVRenderApp('desktop-browser')).toBe(fallbackApp); + expect(createStage).toHaveBeenCalledTimes(2); + expect(firstStage).not.toBe(secondStage); + + first.release(); + + expect(releaseSecondStage).not.toHaveBeenCalled(); + expect(secondStage.window).toBeDefined(); + expect(secondStage.defaultLayer).toBeDefined(); + + second.release(); + + expect(releaseSecondStage).toHaveBeenCalledTimes(1); + removeDom(secondCanvasDom); + }); + + it('should keep the default dom render path working with fallback app stage creation', () => { + const dom = createDiv(); + const chart = new VChart(spec(), { + dom, + animation: false + }); + charts.push(chart); + + chart.renderSync(); + chart.updateSpecSync(spec(9)); + + expect(chart.getStage()).toBeDefined(); + + chart.release(); + removeDom(dom); + }); + }); }); diff --git a/packages/vchart/package.json b/packages/vchart/package.json index ef9bf65d99..477dd24a59 100644 --- a/packages/vchart/package.json +++ b/packages/vchart/package.json @@ -126,10 +126,10 @@ "@visactor/vdataset": "~1.0.23", "@visactor/vscale": "~1.0.23", "@visactor/vlayouts": "~1.0.23", - "@visactor/vrender-core": "1.0.45", - "@visactor/vrender-kits": "1.0.45", - "@visactor/vrender-components": "1.0.45", - "@visactor/vrender-animate": "1.0.45", + "@visactor/vrender-core": "1.1.0-alpha.2", + "@visactor/vrender-kits": "1.1.0-alpha.2", + "@visactor/vrender-components": "1.1.0-alpha.2", + "@visactor/vrender-animate": "1.1.0-alpha.2", "@visactor/vutils-extension": "workspace:2.0.22" }, "publishConfig": { diff --git a/packages/vchart/src/compile/compiler.ts b/packages/vchart/src/compile/compiler.ts index 881086fac9..9a4d257c86 100644 --- a/packages/vchart/src/compile/compiler.ts +++ b/packages/vchart/src/compile/compiler.ts @@ -9,7 +9,7 @@ import type { IBoundsLike } from '@visactor/vutils'; import { array, isArray, isObject, isValid } from '@visactor/vutils'; import type { EventSourceType } from '../event/interface'; import type { IChart } from '../chart/interface'; -import { createGroup, Stage, vglobal, waitForAllSubLayers } from '@visactor/vrender-core'; +import { createGroup, vglobal, waitForAllSubLayers } from '@visactor/vrender-core'; import type { IColor, IEventTarget, IGroup, IStage } from '@visactor/vrender-core'; import type { IMorphConfig } from '../animation/spec'; import type { IVChart, IVChartRenderOption } from '../core/interface'; @@ -23,6 +23,7 @@ import { log } from '../util/debug'; import type { MarkAnimationSpec, TypeAnimationConfig } from '../animation/interface'; import { AnimationStateEnum } from '../animation/interface'; import { BuiltIn_DISAPPEAR_ANIMATE_NAME } from '../constant/animate'; +import { createStageFromApp, resolveVRenderApp } from './stage-app'; type EventListener = { type: string; @@ -47,6 +48,8 @@ export class Compiler implements ICompiler { protected _stage: IStage; + private _isExternalStage: boolean = false; + protected _stateAnimationConfig: Partial; get stateAnimationConfig() { return this._stateAnimationConfig; @@ -127,9 +130,13 @@ export class Compiler implements ICompiler { background } = this._option; vglobal.setEnv(toRenderMode(mode), modeParams ?? {}); - this._stage = - this._option.stage ?? - (new Stage({ + const externalStage = this._option.stage; + this._isExternalStage = !!externalStage; + this._stage = externalStage; + + if (!this._stage) { + const app = resolveVRenderApp(this._option.app, mode); + this._stage = createStageFromApp(app, { background, width: this._width, height: this._height, @@ -158,7 +165,8 @@ export class Compiler implements ICompiler { ReactDOM: this._option.ReactDOM, autoRefresh: isValid(autoRefreshDpr) ? autoRefreshDpr : !isValid(dpr), ...(this._option.renderHooks ?? {}) - }) as unknown as IStage); + }) as unknown as IStage; + } this._stage.enableIncrementalAutoRender(); @@ -726,17 +734,27 @@ export class Compiler implements ICompiler { } release(): void { + const stage = this._stage; + const rootGroup = this._rootGroup; + const shouldReleaseStage = !!stage && !this._isExternalStage; + this.clearNextRender(); this.releaseEvent(); - this._option = this._container = null as any; // vgrammar release this.releaseGrammar(true); - if (this._stage !== this._option?.stage) { - // don't release the stage created by outside - this._stage.release(); + if (stage) { + if (shouldReleaseStage) { + stage.release(); + } else if (rootGroup) { + stage.defaultLayer.removeChild(rootGroup); + rootGroup.release(); + } } this._stage = null; + this._rootGroup = null; + this._isExternalStage = false; + this._option = this._container = null as any; this.isInited = false; this._compileChart = null; diff --git a/packages/vchart/src/compile/interface/compiler.ts b/packages/vchart/src/compile/interface/compiler.ts index 9d5df61fc5..6380d1b1fe 100644 --- a/packages/vchart/src/compile/interface/compiler.ts +++ b/packages/vchart/src/compile/interface/compiler.ts @@ -1,4 +1,4 @@ -import type { IColor, IStageParams, IStage, ILayer, IOption3D, ITicker } from '@visactor/vrender-core'; +import type { IApp, IColor, IStageParams, IStage, ILayer, IOption3D, ITicker } from '@visactor/vrender-core'; import type { IPerformanceHook, RenderMode } from '../../typings/spec/common'; import type { IBoundsLike } from '@visactor/vutils'; import type { StringOrNumber } from '../../typings'; @@ -114,9 +114,15 @@ export interface IRenderOption { */ canvasControled?: boolean; /** - * 外部传入的 VRender stage + * 外部传入的 VRender stage。普通用户仍应优先使用 dom/renderCanvas; + * 显式传入 stage 时,stage ownership 属于外部,VChart release 不会释放该 stage。 */ stage?: IStage; + /** + * 外部传入的 VRender app。VChart 可使用它创建内部 stage,但 app ownership 属于外部, + * VChart release 只释放自己创建的 stage,不释放该 app。 + */ + app?: IApp; /** * 外部传入的 VRender layer */ diff --git a/packages/vchart/src/compile/stage-app.ts b/packages/vchart/src/compile/stage-app.ts new file mode 100644 index 0000000000..4cee1db3fc --- /dev/null +++ b/packages/vchart/src/compile/stage-app.ts @@ -0,0 +1,51 @@ +import { createBrowserApp, createNodeApp, type IApp, type IStage, type IStageParams } from '@visactor/vrender-core'; +import { + installBrowserEnvToApp, + installBrowserPickersToApp, + installDefaultGraphicsToApp, + installNodeEnvToApp, + installNodePickersToApp +} from '@visactor/vrender-kits'; +import { RenderModeEnum, type RenderMode } from '../typings/spec/common'; + +type VRenderAppEnv = 'browser' | 'node'; + +const defaultVRenderApps = new Map(); + +const getVRenderAppEnv = (mode?: RenderMode): VRenderAppEnv => + mode === RenderModeEnum.node || mode === RenderModeEnum.worker ? 'node' : 'browser'; + +// Default apps are an internal reuse detail; ordinary VChart users should keep using dom/renderCanvas. +const createDefaultVRenderApp = (env: VRenderAppEnv): IApp => { + const app = env === 'node' ? createNodeApp() : createBrowserApp(); + + if (env === 'node') { + installNodeEnvToApp(app); + installDefaultGraphicsToApp(app); + installNodePickersToApp(app); + } else { + installBrowserEnvToApp(app); + installDefaultGraphicsToApp(app); + installBrowserPickersToApp(app); + } + + return app; +}; + +export const getDefaultVRenderApp = (mode?: RenderMode): IApp => { + const env = getVRenderAppEnv(mode); + const app = defaultVRenderApps.get(env); + + if (app && !app.released) { + return app; + } + + const nextApp = createDefaultVRenderApp(env); + defaultVRenderApps.set(env, nextApp); + + return nextApp; +}; + +export const resolveVRenderApp = (app: IApp | undefined, mode?: RenderMode): IApp => app ?? getDefaultVRenderApp(mode); + +export const createStageFromApp = (app: IApp, params: Partial): IStage => app.createStage(params); diff --git a/tools/story-player/package.json b/tools/story-player/package.json index 3cc90f174c..6339cca92c 100644 --- a/tools/story-player/package.json +++ b/tools/story-player/package.json @@ -56,10 +56,10 @@ "vite": "3.2.6" }, "dependencies": { - "@visactor/vrender-core": "1.0.45", - "@visactor/vrender-kits": "1.0.45", + "@visactor/vrender-core": "1.1.0-alpha.2", + "@visactor/vrender-kits": "1.1.0-alpha.2", "@visactor/vchart": "workspace:2.0.22", - "@visactor/vrender": "1.0.45", + "@visactor/vrender": "1.1.0-alpha.2", "@visactor/vutils": "~1.0.23" } -} \ No newline at end of file +} From ea7a0007c054cf864b585e3bb10adf582e838144 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Mon, 27 Apr 2026 15:49:00 +0800 Subject: [PATCH 2/7] chore: align VChart with VRender alpha app and state Use root app factories for VChart-created stages. Publish ordinary mark state styles as shared VRender state definitions. Keep external stage and app ownership outside VChart release. Constraint: Do not modify VRender or use local VRender2 links Constraint: Keep this as a minimal VChart migration Rejected: Manual app bootstrap | root VRender app creators are public API Rejected: stateProxy normal path | VRender now owns resolvedStatePatch Confidence: high Scope-risk: moderate Directive: Do not use direct addState/removeState for VChart state changes Tested: install --check-only Tested: test --only @visactor/vchart Tested: build --to @visactor/vchart Tested: eslint touched VChart files Not-tested: Browser smoke lacked a local browser driver or harness --- common/config/rush/pnpm-lock.yaml | 3 + .../__tests__/unit/core/vchart-event.test.ts | 220 +++++++++++++++++- .../vchart/__tests__/unit/core/vchart.test.ts | 47 +++- packages/vchart/package.json | 1 + packages/vchart/src/compile/compiler.ts | 76 +++--- packages/vchart/src/compile/stage-app.ts | 88 ++++--- packages/vchart/src/component/brush/brush.ts | 18 +- .../vchart/src/interaction/interaction.ts | 19 +- .../triggers/element-active-by-legend.ts | 3 +- .../interaction/triggers/element-active.ts | 5 +- .../triggers/element-highlight-by-group.ts | 3 +- .../triggers/element-highlight-by-legend.ts | 3 +- .../triggers/element-highlight-by-name.ts | 3 +- .../interaction/triggers/element-highlight.ts | 3 +- .../interaction/triggers/element-select.ts | 5 +- packages/vchart/src/mark/base/base-mark.ts | 88 ++++--- packages/vchart/src/mark/utils/glyph.ts | 5 +- packages/vchart/src/series/sankey/sankey.ts | 71 +++--- packages/vchart/src/util/graphic-state.ts | 32 +++ 19 files changed, 523 insertions(+), 170 deletions(-) create mode 100644 packages/vchart/src/util/graphic-state.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 546b5efda2..6862a761e8 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -528,6 +528,9 @@ importers: '@visactor/vlayouts': specifier: ~1.0.23 version: 1.0.23 + '@visactor/vrender': + specifier: 1.1.0-alpha.2 + version: 1.1.0-alpha.2 '@visactor/vrender-animate': specifier: 1.1.0-alpha.2 version: 1.1.0-alpha.2 diff --git a/packages/vchart/__tests__/unit/core/vchart-event.test.ts b/packages/vchart/__tests__/unit/core/vchart-event.test.ts index 2896595ed3..2483e9eb15 100644 --- a/packages/vchart/__tests__/unit/core/vchart-event.test.ts +++ b/packages/vchart/__tests__/unit/core/vchart-event.test.ts @@ -3,6 +3,7 @@ import VChart, { type BaseEventParams, type IBarChartSpec, type IChart, + type ILineChartSpec, type ICommonChartSpec, type IMark, type IMarkGraphic, @@ -10,6 +11,22 @@ import VChart, { } from '../../../src'; import { createDiv, removeDom } from '../../util/dom'; +type StateGraphic = IMarkGraphic & { + attribute: Record; + resolvedStatePatch?: Record; + stateProxy?: (stateName: string, states: string[]) => Record; +}; + +type SharedStateDefinitions = Record< + string, + { + resolver?: (context: { graphic: IMarkGraphic }) => Record | undefined; + } +>; + +const getSharedStateDefinitions = (mark: IMark) => + (mark.getProduct() as unknown as { sharedStateDefinitions?: SharedStateDefinitions }).sharedStateDefinitions; + describe('vchart event test', () => { let container: HTMLElement; let dom: HTMLElement; @@ -361,15 +378,14 @@ describe('vchart event test', () => { throw new Error('Expected bar mark to exist'); } - const barGraphic = barMark.getGraphics()[0] as IMarkGraphic & { - attribute: Record; - resolvedStatePatch?: Record; - stateProxy?: (stateName: string, states: string[]) => Record; - }; + const barGraphic = barMark.getGraphics()[0] as StateGraphic; expect(barGraphic).toBeDefined(); - expect(typeof barGraphic.stateProxy).toBe('function'); + const sharedStateDefinitions = getSharedStateDefinitions(barMark); + expect(typeof sharedStateDefinitions?.hover?.resolver).toBe('function'); + expect(typeof sharedStateDefinitions?.selected?.resolver).toBe('function'); + expect(barGraphic.stateProxy).toBeFalsy(); - const hoverPatch = barGraphic.stateProxy?.('hover', ['hover']); + const hoverPatch = sharedStateDefinitions.hover.resolver({ graphic: barGraphic }); expect(hoverPatch?.fillOpacity).toBe(0.31); chart.getEvent().emit('pointerover', { item: barGraphic } as unknown as BaseEventParams); @@ -384,7 +400,7 @@ describe('vchart event test', () => { expect(barGraphic.resolvedStatePatch).toBeUndefined(); expect(barGraphic.attribute.fillOpacity).not.toBe(0.31); - const selectedPatch = barGraphic.stateProxy?.('selected', ['selected']); + const selectedPatch = sharedStateDefinitions.selected.resolver({ graphic: barGraphic }); expect(selectedPatch?.stroke).toBe('#111827'); expect(selectedPatch?.lineWidth).toBe(4); @@ -395,6 +411,194 @@ describe('vchart event test', () => { expect(barGraphic.resolvedStatePatch?.lineWidth).toBe(4); expect(barGraphic.attribute.stroke).toBe('#111827'); expect(barGraphic.attribute.lineWidth).toBe(4); + + chartWithState.updateSpecSync({ + ...(chartWithState.getSpec() as IBarChartSpec), + data: [ + { + id: 'data', + values: [ + { x: 'Mon', y: 14 }, + { x: 'Tue', y: 16 } + ] + } + ] + }); + + barGraphic.useStates([]); + + expect(barGraphic.resolvedStatePatch).toBeUndefined(); + expect(barGraphic.attribute.stroke).not.toBe('#111827'); + expect(barGraphic.attribute.lineWidth).not.toBe(4); + } finally { + chartWithState.release(); + removeDom(stateContainer); + } + }); + + it('should apply line interaction state style through shared vrender state definitions', () => { + const stateContainer = createDiv(); + const stateDom = createDiv(stateContainer); + + const chartWithState = new VChart( + { + type: 'line', + width: 400, + height: 300, + data: [ + { + id: 'data', + values: [ + { x: 'Mon', y: 10 }, + { x: 'Tue', y: 12 } + ] + } + ], + xField: 'x', + yField: 'y', + hover: { enable: true, trigger: 'pointerover', triggerOff: 'pointerout' }, + select: { enable: true, trigger: 'click', mode: 'single' }, + line: { + state: { + hover: { + stroke: '#ea580c' + }, + selected: { + lineWidth: 5 + } + } + } + } as ILineChartSpec, + { + dom: stateDom, + animation: false + } + ); + + chartWithState.renderSync(); + + try { + const chart = chartWithState.getChart() as IChart; + const lineSeries = chart.getAllSeries()[0]; + const lineMark = lineSeries.getMarks().find((mark: IMark) => mark.name === 'line'); + expect(lineMark).toBeDefined(); + if (!lineMark) { + throw new Error('Expected line mark to exist'); + } + + const lineGraphic = lineMark.getGraphics()[0] as StateGraphic; + const sharedStateDefinitions = getSharedStateDefinitions(lineMark); + expect(typeof sharedStateDefinitions?.hover?.resolver).toBe('function'); + expect(typeof sharedStateDefinitions?.selected?.resolver).toBe('function'); + expect(lineGraphic.stateProxy).toBeFalsy(); + + expect(sharedStateDefinitions.hover.resolver({ graphic: lineGraphic })?.stroke).toBe('#ea580c'); + chart.getEvent().emit('pointerover', { item: lineGraphic } as unknown as BaseEventParams); + expect(lineGraphic.hasState('hover')).toBe(true); + expect(lineGraphic.resolvedStatePatch?.stroke).toBe('#ea580c'); + expect(lineGraphic.attribute.stroke).toBe('#ea580c'); + + chart.getEvent().emit('pointerout', { item: lineGraphic } as unknown as BaseEventParams); + expect(lineGraphic.hasState('hover')).toBe(false); + expect(lineGraphic.resolvedStatePatch).toBeUndefined(); + expect(lineGraphic.attribute.stroke).not.toBe('#ea580c'); + + expect(sharedStateDefinitions.selected.resolver({ graphic: lineGraphic })?.lineWidth).toBe(5); + chart.getEvent().emit('click', { item: lineGraphic } as unknown as BaseEventParams); + expect(lineGraphic.hasState('selected')).toBe(true); + expect(lineGraphic.resolvedStatePatch?.lineWidth).toBe(5); + expect(lineGraphic.attribute.lineWidth).toBe(5); + + lineGraphic.useStates([]); + expect(lineGraphic.resolvedStatePatch).toBeUndefined(); + } finally { + chartWithState.release(); + removeDom(stateContainer); + } + }); + + it('should apply text custom-mark interaction state style through shared vrender state definitions', () => { + const stateContainer = createDiv(); + const stateDom = createDiv(stateContainer); + + const chartWithState = new VChart( + { + type: 'bar', + width: 400, + height: 300, + data: [ + { + id: 'data', + values: [{ x: 'Mon', y: 10 }] + } + ], + xField: 'x', + yField: 'y', + hover: { enable: true, trigger: 'pointerover', triggerOff: 'pointerout' }, + select: { enable: true, trigger: 'click', mode: 'single' }, + customMark: [ + { + type: 'text', + name: 'stateText', + style: { + x: 40, + y: 40, + text: 'state', + fill: '#334155', + fontSize: 14 + }, + state: { + hover: { + fill: '#dc2626' + }, + selected: { + fontSize: 24 + } + } + } + ] + } as IBarChartSpec, + { + dom: stateDom, + animation: false + } + ); + + chartWithState.renderSync(); + + try { + const chart = chartWithState.getChart() as IChart; + const textMark = chart.getAllMarks().find((mark: IMark) => mark.name === 'stateText'); + expect(textMark).toBeDefined(); + if (!textMark) { + throw new Error('Expected text custom mark to exist'); + } + + const textGraphic = textMark.getGraphics()[0] as StateGraphic; + const sharedStateDefinitions = getSharedStateDefinitions(textMark); + expect(typeof sharedStateDefinitions?.hover?.resolver).toBe('function'); + expect(typeof sharedStateDefinitions?.selected?.resolver).toBe('function'); + expect(textGraphic.stateProxy).toBeFalsy(); + + expect(sharedStateDefinitions.hover.resolver({ graphic: textGraphic })?.fill).toBe('#dc2626'); + textGraphic.useStates(['hover']); + expect(textGraphic.hasState('hover')).toBe(true); + expect(textGraphic.resolvedStatePatch?.fill).toBe('#dc2626'); + expect(textGraphic.attribute.fill).toBe('#dc2626'); + + textGraphic.useStates([]); + expect(textGraphic.hasState('hover')).toBe(false); + expect(textGraphic.resolvedStatePatch).toBeUndefined(); + expect(textGraphic.attribute.fill).not.toBe('#dc2626'); + + expect(sharedStateDefinitions.selected.resolver({ graphic: textGraphic })?.fontSize).toBe(24); + textGraphic.useStates(['selected']); + expect(textGraphic.hasState('selected')).toBe(true); + expect(textGraphic.resolvedStatePatch?.fontSize).toBe(24); + expect(textGraphic.attribute.fontSize).toBe(24); + + textGraphic.useStates([]); + expect(textGraphic.resolvedStatePatch).toBeUndefined(); } finally { chartWithState.release(); removeDom(stateContainer); diff --git a/packages/vchart/__tests__/unit/core/vchart.test.ts b/packages/vchart/__tests__/unit/core/vchart.test.ts index 03a09b6b4e..d07f21e45e 100644 --- a/packages/vchart/__tests__/unit/core/vchart.test.ts +++ b/packages/vchart/__tests__/unit/core/vchart.test.ts @@ -1,9 +1,5 @@ -import { createBrowserApp, Stage, type Group, type IApp, type IArc, type Text } from '@visactor/vrender-core'; -import { - installBrowserEnvToApp, - installBrowserPickersToApp, - installDefaultGraphicsToApp -} from '@visactor/vrender-kits'; +import { Stage, type Group, type IApp, type IArc, type Text } from '@visactor/vrender-core'; +import { createBrowserVRenderApp } from '@visactor/vrender'; import type { IBarChartSpec } from '../../../src'; import { default as VChart } from '../../../src'; import { getDefaultVRenderApp } from '../../../src/compile/stage-app'; @@ -807,10 +803,7 @@ describe('VChart', () => { }; const createExternalApp = () => { - externalApp = createBrowserApp(); - installBrowserEnvToApp(externalApp); - installDefaultGraphicsToApp(externalApp); - installBrowserPickersToApp(externalApp); + externalApp = createBrowserVRenderApp(); rawExternalAppRelease = externalApp.release.bind(externalApp); return externalApp; }; @@ -979,6 +972,40 @@ describe('VChart', () => { removeDom(secondCanvasDom); }); + it('should release the fallback app after the last internally owned stage is released', () => { + const fallbackApp = getDefaultVRenderApp('desktop-browser'); + const rawFallbackAppRelease = fallbackApp.release.bind(fallbackApp); + const releaseFallbackApp = jest.fn(() => rawFallbackAppRelease()); + fallbackApp.release = releaseFallbackApp as any; + const secondCanvasDom = createCanvas(); + secondCanvasDom.width = 200; + secondCanvasDom.height = 150; + + const first = new VChart(spec(), { + renderCanvas: canvasDom, + animation: false + }); + charts.push(first); + first.renderSync(); + + const second = new VChart(spec(6), { + renderCanvas: secondCanvasDom, + animation: false + }); + charts.push(second); + second.renderSync(); + + first.release(); + + expect(releaseFallbackApp).not.toHaveBeenCalled(); + + second.release(); + + expect(releaseFallbackApp).toHaveBeenCalledTimes(1); + expect(getDefaultVRenderApp('desktop-browser')).not.toBe(fallbackApp); + removeDom(secondCanvasDom); + }); + it('should keep the default dom render path working with fallback app stage creation', () => { const dom = createDiv(); const chart = new VChart(spec(), { diff --git a/packages/vchart/package.json b/packages/vchart/package.json index 477dd24a59..b684b8e7c0 100644 --- a/packages/vchart/package.json +++ b/packages/vchart/package.json @@ -126,6 +126,7 @@ "@visactor/vdataset": "~1.0.23", "@visactor/vscale": "~1.0.23", "@visactor/vlayouts": "~1.0.23", + "@visactor/vrender": "1.1.0-alpha.2", "@visactor/vrender-core": "1.1.0-alpha.2", "@visactor/vrender-kits": "1.1.0-alpha.2", "@visactor/vrender-components": "1.1.0-alpha.2", diff --git a/packages/vchart/src/compile/compiler.ts b/packages/vchart/src/compile/compiler.ts index 9a4d257c86..54dbf1afeb 100644 --- a/packages/vchart/src/compile/compiler.ts +++ b/packages/vchart/src/compile/compiler.ts @@ -50,6 +50,8 @@ export class Compiler implements ICompiler { private _isExternalStage: boolean = false; + private _releaseVRenderAppRef?: () => void; + protected _stateAnimationConfig: Partial; get stateAnimationConfig() { return this._stateAnimationConfig; @@ -132,40 +134,49 @@ export class Compiler implements ICompiler { vglobal.setEnv(toRenderMode(mode), modeParams ?? {}); const externalStage = this._option.stage; this._isExternalStage = !!externalStage; + this._releaseVRenderAppRef = undefined; this._stage = externalStage; if (!this._stage) { - const app = resolveVRenderApp(this._option.app, mode); - this._stage = createStageFromApp(app, { - background, - width: this._width, - height: this._height, - container: this._container.dom ?? null, - canvas: this._container.canvas ?? null, - dpr, - viewBox: this._option.viewBox, - canvasControled: this._option.canvasControled, - beforeRender: (stage: IStage) => { - this._compileChart?.onBeforeRender(); - this._option.beforeRender?.(stage); - }, - afterRender: this._option.afterRender, - disableDirtyBounds: true, - autoRender: true, - ticker: this._option.ticker, - pluginList: this._option.pluginList, - enableHtmlAttribute: this._option.enableHtmlAttribute, - optimize: this._option.optimize, - supportsTouchEvents: this._option.supportsTouchEvents, - supportsPointerEvents: this._option.supportsPointerEvents, - event: { - clickInterval: clickInterval, - autoPreventDefault: autoPreventDefault - }, - ReactDOM: this._option.ReactDOM, - autoRefresh: isValid(autoRefreshDpr) ? autoRefreshDpr : !isValid(dpr), - ...(this._option.renderHooks ?? {}) - }) as unknown as IStage; + const resolvedApp = resolveVRenderApp(this._option.app, mode); + this._releaseVRenderAppRef = resolvedApp.releaseAppRef; + + try { + this._stage = createStageFromApp(resolvedApp.app, { + background, + width: this._width, + height: this._height, + container: this._container.dom ?? null, + canvas: this._container.canvas ?? null, + dpr, + viewBox: this._option.viewBox, + canvasControled: this._option.canvasControled, + beforeRender: (stage: IStage) => { + this._compileChart?.onBeforeRender(); + this._option.beforeRender?.(stage); + }, + afterRender: this._option.afterRender, + disableDirtyBounds: true, + autoRender: true, + ticker: this._option.ticker, + pluginList: this._option.pluginList, + enableHtmlAttribute: this._option.enableHtmlAttribute, + optimize: this._option.optimize, + supportsTouchEvents: this._option.supportsTouchEvents, + supportsPointerEvents: this._option.supportsPointerEvents, + event: { + clickInterval: clickInterval, + autoPreventDefault: autoPreventDefault + }, + ReactDOM: this._option.ReactDOM, + autoRefresh: isValid(autoRefreshDpr) ? autoRefreshDpr : !isValid(dpr), + ...(this._option.renderHooks ?? {}) + }) as unknown as IStage; + } catch (error) { + this._releaseVRenderAppRef?.(); + this._releaseVRenderAppRef = undefined; + throw error; + } } this._stage.enableIncrementalAutoRender(); @@ -737,6 +748,7 @@ export class Compiler implements ICompiler { const stage = this._stage; const rootGroup = this._rootGroup; const shouldReleaseStage = !!stage && !this._isExternalStage; + const releaseVRenderAppRef = this._releaseVRenderAppRef; this.clearNextRender(); this.releaseEvent(); @@ -751,9 +763,11 @@ export class Compiler implements ICompiler { rootGroup.release(); } } + releaseVRenderAppRef?.(); this._stage = null; this._rootGroup = null; this._isExternalStage = false; + this._releaseVRenderAppRef = undefined; this._option = this._container = null as any; this.isInited = false; diff --git a/packages/vchart/src/compile/stage-app.ts b/packages/vchart/src/compile/stage-app.ts index 4cee1db3fc..9f6ecb6853 100644 --- a/packages/vchart/src/compile/stage-app.ts +++ b/packages/vchart/src/compile/stage-app.ts @@ -1,51 +1,79 @@ -import { createBrowserApp, createNodeApp, type IApp, type IStage, type IStageParams } from '@visactor/vrender-core'; -import { - installBrowserEnvToApp, - installBrowserPickersToApp, - installDefaultGraphicsToApp, - installNodeEnvToApp, - installNodePickersToApp -} from '@visactor/vrender-kits'; +import { createBrowserVRenderApp, createNodeVRenderApp } from '@visactor/vrender'; +import type { IApp, IStage, IStageParams } from '@visactor/vrender-core'; import { RenderModeEnum, type RenderMode } from '../typings/spec/common'; type VRenderAppEnv = 'browser' | 'node'; -const defaultVRenderApps = new Map(); +type DefaultVRenderAppRecord = { + app: IApp; + refCount: number; +}; + +export type ResolvedVRenderApp = { + app: IApp; + releaseAppRef?: () => void; +}; + +const defaultVRenderApps = new Map(); const getVRenderAppEnv = (mode?: RenderMode): VRenderAppEnv => mode === RenderModeEnum.node || mode === RenderModeEnum.worker ? 'node' : 'browser'; // Default apps are an internal reuse detail; ordinary VChart users should keep using dom/renderCanvas. -const createDefaultVRenderApp = (env: VRenderAppEnv): IApp => { - const app = env === 'node' ? createNodeApp() : createBrowserApp(); - - if (env === 'node') { - installNodeEnvToApp(app); - installDefaultGraphicsToApp(app); - installNodePickersToApp(app); - } else { - installBrowserEnvToApp(app); - installDefaultGraphicsToApp(app); - installBrowserPickersToApp(app); +const createDefaultVRenderApp = (env: VRenderAppEnv): IApp => + env === 'node' ? createNodeVRenderApp() : createBrowserVRenderApp(); + +const getDefaultVRenderAppRecord = (mode?: RenderMode): DefaultVRenderAppRecord => { + const env = getVRenderAppEnv(mode); + const record = defaultVRenderApps.get(env); + + if (record && !record.app.released) { + return record; } - return app; + const nextRecord = { + app: createDefaultVRenderApp(env), + refCount: 0 + }; + defaultVRenderApps.set(env, nextRecord); + + return nextRecord; }; -export const getDefaultVRenderApp = (mode?: RenderMode): IApp => { +export const getDefaultVRenderApp = (mode?: RenderMode): IApp => getDefaultVRenderAppRecord(mode).app; + +const retainDefaultVRenderApp = (mode?: RenderMode): ResolvedVRenderApp => { const env = getVRenderAppEnv(mode); - const app = defaultVRenderApps.get(env); + const record = getDefaultVRenderAppRecord(mode); + let released = false; - if (app && !app.released) { - return app; - } + record.refCount += 1; - const nextApp = createDefaultVRenderApp(env); - defaultVRenderApps.set(env, nextApp); + return { + app: record.app, + releaseAppRef: () => { + if (released) { + return; + } + released = true; + record.refCount -= 1; - return nextApp; + if (record.refCount <= 0) { + defaultVRenderApps.delete(env); + if (!record.app.released) { + record.app.release(); + } + } + } + }; }; -export const resolveVRenderApp = (app: IApp | undefined, mode?: RenderMode): IApp => app ?? getDefaultVRenderApp(mode); +export const resolveVRenderApp = (app: IApp | undefined, mode?: RenderMode): ResolvedVRenderApp => { + if (app) { + return { app }; + } + + return retainDefaultVRenderApp(mode); +}; export const createStageFromApp = (app: IApp, params: Partial): IStage => app.createStage(params); diff --git a/packages/vchart/src/component/brush/brush.ts b/packages/vchart/src/component/brush/brush.ts index d130fc57d1..686a974702 100644 --- a/packages/vchart/src/component/brush/brush.ts +++ b/packages/vchart/src/component/brush/brush.ts @@ -25,6 +25,7 @@ import { brush } from '../../theme/builtin/common/component/brush'; import { isReverse, statePointToData } from '../data-zoom/util'; import type { CartesianAxis } from '../axis/cartesian'; import type { IRenderOption } from '../../compile/interface'; +import { addGraphicState, removeGraphicState } from '../../util/graphic-state'; const IN_BRUSH_STATE = 'inBrush'; const OUT_BRUSH_STATE = 'outOfBrush'; @@ -499,15 +500,15 @@ export class Brush extends BaseComponent i // now: 不在当前brush中 const isBrushContainItem = this._isBrushContainItem(operateMask.globalAABBBounds, pointsCoord, graphicItem); if (this._outOfBrushElementsMap?.[elementKey] && isBrushContainItem) { - graphicItem.addState(IN_BRUSH_STATE, true); + addGraphicState(graphicItem, IN_BRUSH_STATE, true); if (!this._inBrushElementsMap[operateMask?.name]) { this._inBrushElementsMap[operateMask?.name] = {}; } this._inBrushElementsMap[operateMask?.name][elementKey] = graphicItem; delete this._outOfBrushElementsMap[elementKey]; } else if (this._inBrushElementsMap?.[operateMask?.name]?.[elementKey] && !isBrushContainItem) { - graphicItem.removeState(IN_BRUSH_STATE); - graphicItem.addState(OUT_BRUSH_STATE, true); + removeGraphicState(graphicItem, IN_BRUSH_STATE); + addGraphicState(graphicItem, OUT_BRUSH_STATE, true); this._outOfBrushElementsMap[elementKey] = graphicItem; delete this._inBrushElementsMap[operateMask.name][elementKey]; } @@ -577,7 +578,7 @@ export class Brush extends BaseComponent i this._linkedOutOfBrushElementsMap?.[elementKey] && this._isBrushContainItem(operateMask.globalAABBBounds, pointsCoord, graphicItem) ) { - graphicItem.addState(IN_BRUSH_STATE, true); + addGraphicState(graphicItem, IN_BRUSH_STATE, true); if (!this._linkedInBrushElementsMap[operateMask?.name]) { this._linkedInBrushElementsMap[operateMask?.name] = {}; } @@ -587,8 +588,8 @@ export class Brush extends BaseComponent i this._linkedInBrushElementsMap?.[operateMask?.name]?.[elementKey] && !this._isBrushContainItem(operateMask.globalAABBBounds, pointsCoord, graphicItem) ) { - graphicItem.removeState(IN_BRUSH_STATE); - graphicItem.addState(OUT_BRUSH_STATE, true); + removeGraphicState(graphicItem, IN_BRUSH_STATE); + addGraphicState(graphicItem, OUT_BRUSH_STATE, true); this._linkedOutOfBrushElementsMap[elementKey] = graphicItem; } }); @@ -643,9 +644,8 @@ export class Brush extends BaseComponent i } graphics.forEach((el: IMarkGraphic) => { const elementKey = mark.id + '_' + el.context.key; - el.removeState(IN_BRUSH_STATE); - el.removeState(OUT_BRUSH_STATE); - stateName && el.addState(stateName, true); + removeGraphicState(el, [IN_BRUSH_STATE, OUT_BRUSH_STATE]); + stateName && addGraphicState(el, stateName, true); elementMap[elementKey] = el; }); }); diff --git a/packages/vchart/src/interaction/interaction.ts b/packages/vchart/src/interaction/interaction.ts index b70d7dace8..c90456cd11 100644 --- a/packages/vchart/src/interaction/interaction.ts +++ b/packages/vchart/src/interaction/interaction.ts @@ -2,6 +2,7 @@ import type { StateValue } from '../compile/mark'; import type { IMarkGraphic } from '../mark/interface'; import type { IInteraction } from './interface/common'; import type { ITrigger } from './interface/trigger'; +import { addGraphicState, removeGraphicState } from '../util/graphic-state'; export class Interaction implements IInteraction { private _stateGraphicsByTrigger: Map = new Map(); @@ -89,7 +90,7 @@ export class Interaction implements IInteraction { if (hasReverse) { const m = g.parent?.mark; const hasAnimation = (m as any).hasAnimationByState && (m as any).hasAnimationByState('state'); - g.addState(reverseState, true, hasAnimation); + addGraphicState(g, reverseState, true, hasAnimation); } }); @@ -100,7 +101,7 @@ export class Interaction implements IInteraction { if (hasReverse) { const m = g.parent?.mark; const hasAnimation = (m as any).hasAnimationByState && (m as any).hasAnimationByState('state'); - g.removeState(reverseState, hasAnimation); + removeGraphicState(g, reverseState, hasAnimation); } }); } @@ -119,7 +120,7 @@ export class Interaction implements IInteraction { if (hasState) { const m = g.parent?.mark; const hasAnimation = (m as any).hasAnimationByState && (m as any).hasAnimationByState('state'); - g.removeState(state, hasAnimation); + removeGraphicState(g, state, hasAnimation); } }); @@ -128,7 +129,7 @@ export class Interaction implements IInteraction { if (hasState) { const m = g.parent?.mark; const hasAnimation = (m as any).hasAnimationByState && (m as any).hasAnimationByState('state'); - g.addState(state, true, hasAnimation); + addGraphicState(g, state, true, hasAnimation); } }); } @@ -155,11 +156,11 @@ export class Interaction implements IInteraction { const isStated = statedGraphics && statedGraphics.includes(g); if (isStated) { if (hasState) { - g.addState(state, true, hasAnimation); + addGraphicState(g, state, true, hasAnimation); } } else { if (hasReverse) { - g.addState(reverseState, true, hasAnimation); + addGraphicState(g, reverseState, true, hasAnimation); } } }); @@ -184,7 +185,7 @@ export class Interaction implements IInteraction { if (isStated) { if (hasState) { - g.addState(state, true, hasAnimation); + addGraphicState(g, state, true, hasAnimation); } } }); @@ -211,14 +212,14 @@ export class Interaction implements IInteraction { if (graphics && graphics.length) { if (reverseState && markIdByState[reverseState] && markIdByState[reverseState].includes(mark.id)) { graphics.forEach(g => { - g.removeState(reverseState, hasAnimation); + removeGraphicState(g, reverseState, hasAnimation); }); } if (state && markIdByState[state] && markIdByState[state].includes(mark.id)) { graphics.forEach(g => { if (statedGraphics.includes(g)) { - g.removeState(state, hasAnimation); + removeGraphicState(g, state, hasAnimation); } }); } diff --git a/packages/vchart/src/interaction/triggers/element-active-by-legend.ts b/packages/vchart/src/interaction/triggers/element-active-by-legend.ts index 160461fb2c..a9a317bd0a 100644 --- a/packages/vchart/src/interaction/triggers/element-active-by-legend.ts +++ b/packages/vchart/src/interaction/triggers/element-active-by-legend.ts @@ -7,6 +7,7 @@ import { generateFilterValue } from './util'; import type { IMarkGraphic } from '../../mark/interface'; import type { BaseEventParams } from '../../core'; import { STATE_VALUE_ENUM } from '../../compile/mark/interface'; +import { removeGraphicState } from '../../util/graphic-state'; const type = 'element-active-by-legend'; const defaultOptions: Partial = { @@ -80,7 +81,7 @@ export class ElementActiveByLegend if (g) { const statedGraphics = interaction.getStatedGraphics(this); if (statedGraphics && statedGraphics.includes(g)) { - g.removeState(state); + removeGraphicState(g, state); interaction.setStatedGraphics( this, statedGraphics.filter(sg => sg !== g) diff --git a/packages/vchart/src/interaction/triggers/element-active.ts b/packages/vchart/src/interaction/triggers/element-active.ts index 02a450693d..74a5497cf0 100644 --- a/packages/vchart/src/interaction/triggers/element-active.ts +++ b/packages/vchart/src/interaction/triggers/element-active.ts @@ -5,6 +5,7 @@ import { TRIGGER_TYPE_ENUM } from './enum'; import { STATE_VALUE_ENUM } from '../../compile/mark/interface'; import type { IMarkGraphic } from '../../mark/interface'; import type { BaseEventParams } from '../../event/interface'; +import { addGraphicState, removeGraphicState } from '../../util/graphic-state'; const defaultOptions: Partial = { state: STATE_VALUE_ENUM.STATE_ACTIVE, @@ -43,7 +44,7 @@ export class ElementActive extends BaseTrigger implements const { state, interaction } = this.options; if (this.isGraphicInStateMark(g, state)) { - g.addState(state, true); + addGraphicState(g, state, true); interaction.setStatedGraphics(this, [g]); } @@ -57,7 +58,7 @@ export class ElementActive extends BaseTrigger implements const g = graphic ?? statedGraphics?.[0]; if (g && statedGraphics?.includes(g)) { - g.removeState(state); + removeGraphicState(g, state); interaction.setStatedGraphics( this, statedGraphics.filter(sg => sg !== g) diff --git a/packages/vchart/src/interaction/triggers/element-highlight-by-group.ts b/packages/vchart/src/interaction/triggers/element-highlight-by-group.ts index 95d75dfa21..6f58a5f5bf 100644 --- a/packages/vchart/src/interaction/triggers/element-highlight-by-group.ts +++ b/packages/vchart/src/interaction/triggers/element-highlight-by-group.ts @@ -5,6 +5,7 @@ import type { IMarkGraphic } from '../../mark/interface'; import { isNil } from '@visactor/vutils'; import type { BaseEventParams } from '../../core'; import { highlightDefaultOptions } from './util'; +import { removeGraphicState } from '../../util/graphic-state'; const type = 'element-highlight-by-group'; @@ -84,7 +85,7 @@ export class ElementHighlightByGroup const { interaction } = this.options; const statedGraphics = interaction.getStatedGraphics(this); - g.removeState([this.options.highlightState, this.options.blurState]); + removeGraphicState(g, [this.options.highlightState, this.options.blurState]); interaction.setStatedGraphics( this, diff --git a/packages/vchart/src/interaction/triggers/element-highlight-by-legend.ts b/packages/vchart/src/interaction/triggers/element-highlight-by-legend.ts index 43065be4e9..b1032a83cc 100644 --- a/packages/vchart/src/interaction/triggers/element-highlight-by-legend.ts +++ b/packages/vchart/src/interaction/triggers/element-highlight-by-legend.ts @@ -6,6 +6,7 @@ import { ChartEvent } from '../../constant/event'; import { generateFilterValue } from './util'; import type { IMarkGraphic } from '../../mark/interface/common'; import type { BaseEventParams } from '../../event/interface'; +import { removeGraphicState } from '../../util/graphic-state'; const type = 'element-highlight-by-legend'; const defaultOptions: Partial = { @@ -84,7 +85,7 @@ export class ElementHighlightByLegend if (g) { const statedGraphics = interaction.getStatedGraphics(this); if (statedGraphics && statedGraphics.includes(g)) { - g.removeState([highlightState, blurState]); + removeGraphicState(g, [highlightState, blurState]); interaction.setStatedGraphics( this, statedGraphics.filter(sg => sg !== g) diff --git a/packages/vchart/src/interaction/triggers/element-highlight-by-name.ts b/packages/vchart/src/interaction/triggers/element-highlight-by-name.ts index 8b948c930c..6667b8ca1e 100644 --- a/packages/vchart/src/interaction/triggers/element-highlight-by-name.ts +++ b/packages/vchart/src/interaction/triggers/element-highlight-by-name.ts @@ -6,6 +6,7 @@ import type { BaseEventParams } from '../../event/interface'; import { array } from '@visactor/vutils'; import type { IMarkGraphic } from '../../mark/interface'; import { generateFilterValue } from './util'; +import { removeGraphicState } from '../../util/graphic-state'; const type = 'element-highlight-by-name'; const defaultOptions: Partial = { @@ -95,7 +96,7 @@ export class ElementHighlightByName if (g) { const statedGraphics = interaction.getStatedGraphics(this); if (statedGraphics && statedGraphics.includes(g)) { - g.removeState([highlightState, blurState]); + removeGraphicState(g, [highlightState, blurState]); interaction.setStatedGraphics( this, statedGraphics.filter(sg => sg !== g) diff --git a/packages/vchart/src/interaction/triggers/element-highlight.ts b/packages/vchart/src/interaction/triggers/element-highlight.ts index 6a3bcddc80..dab41d2eb3 100644 --- a/packages/vchart/src/interaction/triggers/element-highlight.ts +++ b/packages/vchart/src/interaction/triggers/element-highlight.ts @@ -7,6 +7,7 @@ import { Factory } from '../../core/factory'; import type { GraphicEventType } from '@visactor/vrender-core'; import { TRIGGER_TYPE_ENUM } from './enum'; import type { BaseEventParams } from '../../event/interface'; +import { removeGraphicState } from '../../util/graphic-state'; const defaultOptions: Partial = { highlightState: STATE_VALUE_ENUM.STATE_HIGHLIGHT, @@ -128,7 +129,7 @@ export class ElementHighlight reset(markGraphic: IMarkGraphic, e?: BaseEventParams) { if (markGraphic) { if (this._markSet.getMarkInId(markGraphic.context.markId)) { - markGraphic.removeState([this.options.highlightState, this.options.blurState]); + removeGraphicState(markGraphic, [this.options.highlightState, this.options.blurState]); } } else { this.resetAll(e); diff --git a/packages/vchart/src/interaction/triggers/element-select.ts b/packages/vchart/src/interaction/triggers/element-select.ts index b13b3acfe1..be8eb65fb1 100644 --- a/packages/vchart/src/interaction/triggers/element-select.ts +++ b/packages/vchart/src/interaction/triggers/element-select.ts @@ -7,6 +7,7 @@ import { parseTriggerOffOfSelect } from './util'; import { Factory } from '../../core/factory'; import { TRIGGER_TYPE_ENUM } from './enum'; import type { BaseEventParams } from '../../event/interface'; +import { addGraphicState, removeGraphicState } from '../../util/graphic-state'; const defaultOptions: Partial = { state: STATE_VALUE_ENUM.STATE_SELECTED, @@ -117,7 +118,7 @@ export class ElementSelect extends BaseTrigger implements if (this._timer) { clearTimeout(this._timer); } - markGraphic.addState(state, true); + addGraphicState(markGraphic, state, true); const newStatedGraphics = this.options.interaction.updateStates( this, @@ -143,7 +144,7 @@ export class ElementSelect extends BaseTrigger implements reset(markGraphic: IMarkGraphic, e?: BaseEventParams) { if (markGraphic) { if (this._markSet.getMarkInId(markGraphic.context.markId)) { - markGraphic.removeState([this.options.state, this.options.reverseState]); + removeGraphicState(markGraphic, [this.options.state, this.options.reverseState]); } } else { this.resetAll(e); diff --git a/packages/vchart/src/mark/base/base-mark.ts b/packages/vchart/src/mark/base/base-mark.ts index ea1e199480..25cc20920f 100644 --- a/packages/vchart/src/mark/base/base-mark.ts +++ b/packages/vchart/src/mark/base/base-mark.ts @@ -55,7 +55,14 @@ import { array, degreeToRadian, isArray, isBoolean, isFunction, isNil, isObject, import { curveTypeTransform, groupData, runEncoder } from '../utils/common'; import type { ICompilableInitOption } from '../../compile/interface'; import { LayoutState } from '../../compile/interface'; -import type { IGroupGraphicAttribute, IGraphicAttribute, IGroup, IGraphic } from '@visactor/vrender-core'; +import type { + IGroupGraphicAttribute, + IGraphicAttribute, + IGroup, + IGraphic, + StateDefinitionsInput, + StateResolveContext +} from '@visactor/vrender-core'; import { createGroup, CustomPath2D } from '@visactor/vrender-core'; import { isStateAttrChangeable } from '../../compile/mark/util'; import { Factory } from '../../core/factory'; @@ -70,6 +77,7 @@ import { CompilableData } from '../../compile/data/compilable-data'; import { getDiffAttributesOfGraphic } from '../../util/mark'; import { log } from '../../util/debug'; import { morph as runMorph } from '../../compile/morph'; +import { addGraphicState, removeGraphicState } from '../../util/graphic-state'; export type ExChannelCall = ( key: string | number | symbol, @@ -257,13 +265,13 @@ export class BaseMark extends GrammarItem implements IMar protected _stateSort?: (stateA: string, stateB: string) => number; - declare protected _product: Maybe; + protected declare _product: Maybe; getProduct() { return this._product; } // 保存上一次的mark,用于morph的时候获取上次的图元 - declare protected _lastMark?: IMark; + protected declare _lastMark?: IMark; /** 初始化 mark data */ protected initMarkData(option: ICompilableInitOption) { @@ -526,7 +534,7 @@ export class BaseMark extends GrammarItem implements IMar return this._simpleStyle; } - declare protected _option: IMarkOption; + protected declare _option: IMarkOption; protected _attributeContext: IModelMarkAttributeContext; @@ -1155,8 +1163,8 @@ export class BaseMark extends GrammarItem implements IMar this._keyGetter = isFunction(this.key) ? (this.key as (datum: Datum) => string) : isValid(this.key) - ? (datum: Datum) => datum?.[this.key as string] - : (datum: Datum) => datum?.[DEFAULT_DATA_KEY]; + ? (datum: Datum) => datum?.[this.key as string] + : (datum: Datum) => datum?.[DEFAULT_DATA_KEY]; this._groupKeyGetter = isValid(this._groupKey) ? (datum: Datum) => { return `${datum?.[this._groupKey]}`; @@ -1171,8 +1179,8 @@ export class BaseMark extends GrammarItem implements IMar const animationState = graphicsAnimationStates.every(state => state === AnimationStateEnum.appear) ? AnimationStateEnum.appear : graphicsAnimationStates.every(state => state === AnimationStateEnum.disappear) - ? AnimationStateEnum.disappear - : graphicsAnimationStates[0]; + ? AnimationStateEnum.disappear + : graphicsAnimationStates[0]; return animationState ?? AnimationStateEnum.none; } @@ -1193,7 +1201,7 @@ export class BaseMark extends GrammarItem implements IMar ? { ...config, // 循环动画的优先级定为最高,不会被屏蔽掉 - priority: type === 'normal' ? (config.priority ?? Infinity) : config.priority + priority: type === 'normal' ? config.priority ?? Infinity : config.priority } : config; } @@ -1318,8 +1326,8 @@ export class BaseMark extends GrammarItem implements IMar return diffState === AnimationStateEnum.exit ? AnimationStateEnum.exit : diffState === AnimationStateEnum.update - ? AnimationStateEnum.update - : AnimationStateEnum.appear; + ? AnimationStateEnum.update + : AnimationStateEnum.appear; }); const customizedState = callback(g); @@ -1545,30 +1553,55 @@ export class BaseMark extends GrammarItem implements IMar g.stateProxy = null; if (g.context.diffState === DiffState.enter || g.context.diffState === DiffState.update) { - g.stateProxy = (stateName: string, nexStates: string[]) => { - return this._runEncoderOfGraphic(this._encoderOfState?.[stateName], g); - }; - g.context.states && g.useStates(g.context.states, hasAnimation); } }; + protected _applySharedStateDefinitions() { + if (!this._product) { + return; + } + + const stateNames = Object.keys(this._encoderOfState ?? {}).filter( + stateName => stateName !== 'group' && stateName !== 'update' + ); + + if (!stateNames.length) { + this._product.sharedStateDefinitions = undefined; + return; + } + + const sortedStateNames = this._stateSort ? stateNames.slice().sort(this._stateSort) : stateNames; + const statePriority = new Map(); + + sortedStateNames.forEach((stateName, index) => { + statePriority.set(stateName, index); + }); + + const sharedStateDefinitions: StateDefinitionsInput> = {}; + + stateNames.forEach(stateName => { + const encoder = this._encoderOfState[stateName]; + + sharedStateDefinitions[stateName] = { + priority: statePriority.get(stateName) ?? 0, + declaredAffectedKeys: Object.keys(encoder ?? {}), + resolver: ({ graphic }: StateResolveContext>) => + this._runEncoderOfGraphic(encoder, graphic as IMarkGraphic) + }; + }); + + this._product.sharedStateDefinitions = sharedStateDefinitions; + } + protected _addProgressiveGraphic(parent: IGroup, g: IMarkGraphic) { (parent as IGroup).incrementalAppendChild(g); } protected _runEncoder(graphics: IMarkGraphic[], noGroupEncode?: boolean) { const attrsByGroup = noGroupEncode ? null : this._runGroupEncoder(this._encoderOfState?.group); - graphics.forEach((g, index) => { - let attrs = this._runEncoderOfGraphic(this._encoderOfState?.update, g); - // 此时需要将最终的正确的样式设置给graphic,这样后续的动画目标属性才会正确,否则会动画样式只有默认状态的样式 - g.currentStates?.forEach((_state: string) => { - const stateAttr = this._runEncoderOfGraphic(this._encoderOfState?.[_state], g); - attrs = { - ...attrs, - ...stateAttr - }; - }); + graphics.forEach(g => { + const attrs = this._runEncoderOfGraphic(this._encoderOfState?.update, g); // 配置的优先级高于encoder if (!isNil(this._markConfig.interactive)) { @@ -1754,6 +1787,7 @@ export class BaseMark extends GrammarItem implements IMar renderInner() { this._updateEncoderByState(); + this._applySharedStateDefinitions(); const data = this._data?.getProduct() ?? [{}]; @@ -1818,9 +1852,9 @@ export class BaseMark extends GrammarItem implements IMar this._graphics.forEach(g => { if (this.state.checkOneState(g, g.context.data, stateInfo) === 'in') { - g.addState(key, true, this.hasAnimationByState('state')); + addGraphicState(g, key, true, this.hasAnimationByState('state')); } else { - g.removeState(key, this.hasAnimationByState('state')); + removeGraphicState(g, key, this.hasAnimationByState('state')); } }); } diff --git a/packages/vchart/src/mark/utils/glyph.ts b/packages/vchart/src/mark/utils/glyph.ts index fa249e08fe..44267562eb 100644 --- a/packages/vchart/src/mark/utils/glyph.ts +++ b/packages/vchart/src/mark/utils/glyph.ts @@ -1,4 +1,5 @@ import type { IMarkGraphic } from '../interface/common'; +import { addGraphicState, removeGraphicState } from '../../util/graphic-state'; export const addRuntimeState = ( g: IMarkGraphic, @@ -14,7 +15,7 @@ export const addRuntimeState = ( g.runtimeStateCache[stateName] = attrs; if (g.hasState(stateName)) { - g.removeState(stateName); + removeGraphicState(g, stateName); } - g.addState(stateName, keepCurrentStates, hasAnimation); + addGraphicState(g, stateName, keepCurrentStates, hasAnimation); }; diff --git a/packages/vchart/src/series/sankey/sankey.ts b/packages/vchart/src/series/sankey/sankey.ts index 19989344b8..bcec07cf57 100644 --- a/packages/vchart/src/series/sankey/sankey.ts +++ b/packages/vchart/src/series/sankey/sankey.ts @@ -40,6 +40,7 @@ import type { ILabelSpec } from '../../component'; import { getDatumOfGraphic } from '../../util'; import { addRuntimeState } from '../../mark/utils/glyph'; import { sankey } from '../../theme/builtin/common/series/sankey'; +import { addGraphicState, removeGraphicState } from '../../util/graphic-state'; export class SankeySeries extends CartesianSeries { static readonly type: string = SeriesTypeEnum.sankey; @@ -520,12 +521,10 @@ export class SankeySeries exten // const states = [STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE]; allNodeElements.forEach(el => { - el.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); - el.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + removeGraphicState(el, [STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE]); }); allLinkElements.forEach(el => { - el.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); - el.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + removeGraphicState(el, [STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE]); }); this._needClear = false; @@ -551,19 +550,19 @@ export class SankeySeries exten highlightNodes.push(linkDatum.target); } - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); - linkEl.addState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, true); // 设置上用户配置选中状态 + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + addGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, true); // 设置上用户配置选中状态 } else if (linkDatum.target === nodeDatum.key) { // 上游link if (!highlightNodes.includes(linkDatum.source)) { highlightNodes.push(linkDatum.source); } - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); - linkEl.addState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, true); // 设置上用户配置选中状态 + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + addGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, true); // 设置上用户配置选中状态 } else { - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); - linkEl.addState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); + addGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); } }); } @@ -586,12 +585,12 @@ export class SankeySeries exten } allLinkElements.forEach(linkEl => { if (linkEl === graphic) { - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); addRuntimeState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, { ratio: 1 }); } else { - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); - linkEl.addState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); + addGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); } }); } @@ -717,11 +716,11 @@ export class SankeySeries exten const linkDatum = getDatumOfGraphic(linkEl) as Datum; if (highlightLinks.includes(linkDatum.key ?? linkDatum.index)) { - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); - linkEl.addState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, true); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + addGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, true); } else { - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); - linkEl.addState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); + addGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); } }); } @@ -786,7 +785,7 @@ export class SankeySeries exten }, 0); const ratio = val / linkDatum.value; - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); addRuntimeState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, { ratio }); @@ -803,7 +802,7 @@ export class SankeySeries exten highlightNodes.push(linkDatum.target); } - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); addRuntimeState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, { ratio: upSelectedLink.value / linkDatum.value @@ -812,8 +811,8 @@ export class SankeySeries exten return; } - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); - linkEl.addState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); + addGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); return; }); @@ -843,17 +842,19 @@ export class SankeySeries exten // const states = [STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE]; if (this._linkMark) { allLinkElements.forEach(linkEl => { - // linkEl.removeState(states); - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + removeGraphicState(linkEl, [ + STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, + STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE + ]); }); } if (this._nodeMark) { allNodeElements.forEach(el => { - // el.removeState(states); - el.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); - el.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + removeGraphicState(el, [ + STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, + STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE + ]); }); } } else { @@ -895,7 +896,7 @@ export class SankeySeries exten if (linkDatum.source === curLinkDatum.source && linkDatum.target === curLinkDatum.target) { // 自身 - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); addRuntimeState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, { ratio: 1 }); return; @@ -929,7 +930,7 @@ export class SankeySeries exten }, 0); const ratio = val / linkDatum.value; - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); addRuntimeState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, { ratio }); // 设置默认的部分高亮 @@ -948,7 +949,7 @@ export class SankeySeries exten if (!highlightNodes.includes(linkDatum.target)) { highlightNodes.push(linkDatum.target); } - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE); addRuntimeState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, { ratio: upSelectedLink.value / (linkDatum as Datum).value @@ -956,8 +957,8 @@ export class SankeySeries exten return; } - linkEl.removeState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); - linkEl.addState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); + removeGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS); + addGraphicState(linkEl, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); return; }); @@ -974,12 +975,12 @@ export class SankeySeries exten } graphics.forEach(g => { - g.removeState([STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS]); + removeGraphicState(g, [STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS]); if (highlightNodes.includes((getDatumOfGraphic(g) as Datum).key)) { - g.addState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, true); + addGraphicState(g, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS, true); } else { - g.addState(STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); + addGraphicState(g, STATE_VALUE_ENUM.STATE_SANKEY_EMPHASIS_REVERSE, true); } }); } diff --git a/packages/vchart/src/util/graphic-state.ts b/packages/vchart/src/util/graphic-state.ts new file mode 100644 index 0000000000..3edd772621 --- /dev/null +++ b/packages/vchart/src/util/graphic-state.ts @@ -0,0 +1,32 @@ +import type { IMarkGraphic } from '../mark/interface'; + +const normalizeStates = (states?: string | string[]) => (Array.isArray(states) ? states : states ? [states] : []); + +export const addGraphicState = ( + graphic: IMarkGraphic, + state: string, + keepCurrentStates: boolean = true, + hasAnimation?: boolean +) => { + if (!state) { + return; + } + + const currentStates = keepCurrentStates ? graphic.currentStates ?? [] : []; + const nextStates = keepCurrentStates ? Array.from(new Set([...currentStates, state])) : [state]; + + graphic.useStates(nextStates, hasAnimation); +}; + +export const removeGraphicState = (graphic: IMarkGraphic, state: string | string[], hasAnimation?: boolean) => { + const states = normalizeStates(state); + + if (!states.length) { + return; + } + + const currentStates = (graphic.currentStates ?? []) as string[]; + const nextStates = currentStates.filter((stateName: string) => !states.includes(stateName)); + + graphic.useStates(nextStates, hasAnimation); +}; From 2a1b6ca9a529ac21d503fae73f08f1c52f666094 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Tue, 5 May 2026 13:20:27 +0800 Subject: [PATCH 3/7] chore: enable VChart validation on VRender alpha.8 Consume published VRender alpha.8 packages for PR validation. Refresh VChart direct deps and Rush lockfile only. Constraint: Use npm registry @visactor/vrender*@1.1.0-alpha.8. Constraint: No local VRender2 link, file, workspace, yalc, or pack. Rejected: Local VRender2 linking | PR must test published packages. Confidence: high Scope-risk: moderate Directive: Keep VRender release validation on npm artifacts. Tested: rush update; rush install --check-only. Tested: rush test --only @visactor/vchart. Tested: rush build --to @visactor/vchart; browser smoke. --- common/config/rush/pnpm-lock.yaml | 120 ++++++++++++------------- docs/package.json | 4 +- packages/openinula-vchart/package.json | 4 +- packages/react-vchart/package.json | 4 +- packages/vchart-extension/package.json | 8 +- packages/vchart/package.json | 10 +-- tools/story-player/package.json | 6 +- 7 files changed, 78 insertions(+), 78 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 6862a761e8..acd53bb92f 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -37,11 +37,11 @@ importers: specifier: 1.2.4-alpha.5 version: 1.2.4-alpha.5 '@visactor/vrender': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vtable': specifier: 1.19.0-alpha.0 version: 1.19.0-alpha.0 @@ -203,11 +203,11 @@ importers: specifier: workspace:2.0.22 version: link:../vchart '@visactor/vrender-core': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -294,11 +294,11 @@ importers: specifier: workspace:2.0.22 version: link:../vchart-extension '@visactor/vrender-core': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -529,20 +529,20 @@ importers: specifier: ~1.0.23 version: 1.0.23 '@visactor/vrender': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-animate': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-components': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-core': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vscale': specifier: ~1.0.23 version: 1.0.23 @@ -695,17 +695,17 @@ importers: specifier: ~1.0.23 version: 1.0.23 '@visactor/vrender-animate': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-components': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-core': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -1263,14 +1263,14 @@ importers: specifier: workspace:2.0.22 version: link:../../packages/vchart '@visactor/vrender': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-core': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.2 - version: 1.1.0-alpha.2 + specifier: 1.1.0-alpha.8 + version: 1.1.0-alpha.8 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -3076,29 +3076,29 @@ packages: '@visactor/vrender-animate@1.0.0-alpha.18': resolution: {integrity: sha512-9kTtvp1ef+1t+AtUiza6A7qBQP7SmvOu3/ILGrqs/HGdZVj1XGjbYvD/X/zwKJ3LEb7gGV5fa8x95e4czTvRSA==} - '@visactor/vrender-animate@1.1.0-alpha.2': - resolution: {integrity: sha512-jlegJK92jVvT/PF16nMeRjlN68IoSgvJz/tKgsZVveJCt5276LSm2K5vpIjXNeaEKvmUnKes+45sZZX6kWNC/A==} + '@visactor/vrender-animate@1.1.0-alpha.8': + resolution: {integrity: sha512-T1CxxlF2QXJ9ElfzwV2tidNW2BZrU2kkk4vCEs7tAZbk4blQvFzUKosU+rZnIUXvmUQ1SLcjpGb6nwiJ35qL9Q==} '@visactor/vrender-components@1.0.0-alpha.18': resolution: {integrity: sha512-7Euq+ZfswL74n2pgkaqZSsPxoSa5SPIGyXatN1eUrdzM2Z0kX6U0RcJg01fctvRs4op6WhcecRLqGvnHcBeb9Q==} - '@visactor/vrender-components@1.1.0-alpha.2': - resolution: {integrity: sha512-nmBTBXEPvDRYbvXtOCLZ89+9WK1A+wLsE4RbuKv2omjA9D+XYRE7QZsW3O1g1/XBD8JqDVDiz+tk2Pp11IBBMw==} + '@visactor/vrender-components@1.1.0-alpha.8': + resolution: {integrity: sha512-UgiULMccv93ta4nI3Ee6LO8+LOpiZNDhXAM/xvFbIx9FccpOYWxdQKzH2B7aGzx1HlINz7Pp2+2QWi6+MedPHA==} '@visactor/vrender-core@1.0.0-alpha.18': resolution: {integrity: sha512-0ihtNvCyNkOsWPFgRqowHzq0IcQgS2Wl/nPpKbVtxWKveenwlhA+ZKoQvam6VJyBY7jeNe1pROy0mJMDyVAJQw==} - '@visactor/vrender-core@1.1.0-alpha.2': - resolution: {integrity: sha512-f9BTOK3Rv5yiTxMJMPLV2VYIgwcsakJ/VXCOp88hbWgy/or2pvPW4/42sOLALDMCjSnpd2zGhV2R2vy2+nSD2w==} + '@visactor/vrender-core@1.1.0-alpha.8': + resolution: {integrity: sha512-Wvvr+G9MVCkigaYoK1a7Y29AphgunA9cgGquetMF8Bch9emMv6KeFer2hUgSCRxD90MV53OxNHKtpIKVVSOPkw==} '@visactor/vrender-kits@1.0.0-alpha.18': resolution: {integrity: sha512-Tvolkq+4G8qiPFZo0Aj8M//Yr6jR2h8FNkFEyWM9gbQbEiTkjpmHAJOYnoSsaPtPrcMSlG4EhJSFDk6ymANHVg==} - '@visactor/vrender-kits@1.1.0-alpha.2': - resolution: {integrity: sha512-fXG3SB/PDcmA2oK6FqHTBvb8438BDeeCkpIvzt1WI/lpghzD6wT7Dd3HhY8hxonArDUUqAsorZyFeQFNPezUTQ==} + '@visactor/vrender-kits@1.1.0-alpha.8': + resolution: {integrity: sha512-jBUL5UMlEArtjrbHs0D+cNmr2sNgDpDTVcWNRg43JeK4JZTL+WySx5UkAjkUY03N0arskX96O6g5sQwZG9u5tQ==} - '@visactor/vrender@1.1.0-alpha.2': - resolution: {integrity: sha512-MNW2rTJLgCVa0T4TTavQ+fMJp2PkvkZwxWAwdzdNlFAIX9WriF24L+Xqwu8nvqsJiUz2BEUbgYP+7sTZcn7krA==} + '@visactor/vrender@1.1.0-alpha.8': + resolution: {integrity: sha512-Bn9T9T3yFqpvSS7rVTqvL59A6XPsGDY5TNqJ8K1Mb1GCASfv5YS70NTFmQipvWHrebatWyodKlEEradbflMegA==} '@visactor/vscale@0.18.18': resolution: {integrity: sha512-iRG4kv+5Fv4KX3AxEfV95XU3I6OmF0QizyAhqHxKa7L1MaT+MRvDDk5zHWf1E8gialLbL2xDe3GnT6g/4u5jhA==} @@ -15029,9 +15029,9 @@ snapshots: '@visactor/vrender-core': 1.0.0-alpha.18 '@visactor/vutils': 1.0.4 - '@visactor/vrender-animate@1.1.0-alpha.2': + '@visactor/vrender-animate@1.1.0-alpha.8': dependencies: - '@visactor/vrender-core': 1.1.0-alpha.2 + '@visactor/vrender-core': 1.1.0-alpha.8 '@visactor/vutils': 1.0.23 '@visactor/vrender-components@1.0.0-alpha.18': @@ -15042,11 +15042,11 @@ snapshots: '@visactor/vscale': 1.0.4 '@visactor/vutils': 1.0.4 - '@visactor/vrender-components@1.1.0-alpha.2': + '@visactor/vrender-components@1.1.0-alpha.8': dependencies: - '@visactor/vrender-animate': 1.1.0-alpha.2 - '@visactor/vrender-core': 1.1.0-alpha.2 - '@visactor/vrender-kits': 1.1.0-alpha.2 + '@visactor/vrender-animate': 1.1.0-alpha.8 + '@visactor/vrender-core': 1.1.0-alpha.8 + '@visactor/vrender-kits': 1.1.0-alpha.8 '@visactor/vscale': 1.0.23 '@visactor/vutils': 1.0.23 @@ -15055,7 +15055,7 @@ snapshots: '@visactor/vutils': 1.0.4 color-convert: 2.0.1 - '@visactor/vrender-core@1.1.0-alpha.2': + '@visactor/vrender-core@1.1.0-alpha.8': dependencies: '@visactor/vutils': 1.0.23 color-convert: 2.0.1 @@ -15069,21 +15069,21 @@ snapshots: lottie-web: 5.13.0 roughjs: 4.5.2 - '@visactor/vrender-kits@1.1.0-alpha.2': + '@visactor/vrender-kits@1.1.0-alpha.8': dependencies: '@resvg/resvg-js': 2.4.1 - '@visactor/vrender-core': 1.1.0-alpha.2 + '@visactor/vrender-core': 1.1.0-alpha.8 '@visactor/vutils': 1.0.23 gifuct-js: 2.1.2 lottie-web: 5.13.0 roughjs: 4.6.6 - '@visactor/vrender@1.1.0-alpha.2': + '@visactor/vrender@1.1.0-alpha.8': dependencies: - '@visactor/vrender-animate': 1.1.0-alpha.2 - '@visactor/vrender-components': 1.1.0-alpha.2 - '@visactor/vrender-core': 1.1.0-alpha.2 - '@visactor/vrender-kits': 1.1.0-alpha.2 + '@visactor/vrender-animate': 1.1.0-alpha.8 + '@visactor/vrender-components': 1.1.0-alpha.8 + '@visactor/vrender-core': 1.1.0-alpha.8 + '@visactor/vrender-kits': 1.1.0-alpha.8 '@visactor/vscale@0.18.18': dependencies: diff --git a/docs/package.json b/docs/package.json index 916924de51..98e9ce617c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -19,8 +19,8 @@ "@visactor/vchart-theme": "~1.6.6", "@visactor/vmind": "1.2.4-alpha.5", "@visactor/vutils": "~1.0.23", - "@visactor/vrender": "1.1.0-alpha.2", - "@visactor/vrender-kits": "1.1.0-alpha.2", + "@visactor/vrender": "1.1.0-alpha.8", + "@visactor/vrender-kits": "1.1.0-alpha.8", "@visactor/vtable": "1.19.0-alpha.0", "@visactor/vtable-editors": "1.19.0-alpha.0", "@visactor/vtable-gantt": "1.19.0-alpha.0", diff --git a/packages/openinula-vchart/package.json b/packages/openinula-vchart/package.json index bcc4a86dae..4a6f62d2a5 100644 --- a/packages/openinula-vchart/package.json +++ b/packages/openinula-vchart/package.json @@ -30,8 +30,8 @@ "dependencies": { "@visactor/vchart": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", - "@visactor/vrender-core": "1.1.0-alpha.2", - "@visactor/vrender-kits": "1.1.0-alpha.2", + "@visactor/vrender-core": "1.1.0-alpha.8", + "@visactor/vrender-kits": "1.1.0-alpha.8", "react-is": "^18.2.0" }, "devDependencies": { diff --git a/packages/react-vchart/package.json b/packages/react-vchart/package.json index 3aa5be4964..c9afe14ab1 100644 --- a/packages/react-vchart/package.json +++ b/packages/react-vchart/package.json @@ -36,8 +36,8 @@ "@visactor/vchart": "workspace:2.0.22", "@visactor/vchart-extension": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", - "@visactor/vrender-core": "1.1.0-alpha.2", - "@visactor/vrender-kits": "1.1.0-alpha.2", + "@visactor/vrender-core": "1.1.0-alpha.8", + "@visactor/vrender-kits": "1.1.0-alpha.8", "react-is": "^18.2.0" }, "devDependencies": { diff --git a/packages/vchart-extension/package.json b/packages/vchart-extension/package.json index 25789045fa..5136ef67d1 100644 --- a/packages/vchart-extension/package.json +++ b/packages/vchart-extension/package.json @@ -26,10 +26,10 @@ "start": "ts-node __tests__/runtime/browser/scripts/initVite.ts && vite serve __tests__/runtime/browser" }, "dependencies": { - "@visactor/vrender-core": "1.1.0-alpha.2", - "@visactor/vrender-kits": "1.1.0-alpha.2", - "@visactor/vrender-components": "1.1.0-alpha.2", - "@visactor/vrender-animate": "1.1.0-alpha.2", + "@visactor/vrender-core": "1.1.0-alpha.8", + "@visactor/vrender-kits": "1.1.0-alpha.8", + "@visactor/vrender-components": "1.1.0-alpha.8", + "@visactor/vrender-animate": "1.1.0-alpha.8", "@visactor/vchart": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", "@visactor/vdataset": "~1.0.23", diff --git a/packages/vchart/package.json b/packages/vchart/package.json index b684b8e7c0..6052a4dc7f 100644 --- a/packages/vchart/package.json +++ b/packages/vchart/package.json @@ -126,11 +126,11 @@ "@visactor/vdataset": "~1.0.23", "@visactor/vscale": "~1.0.23", "@visactor/vlayouts": "~1.0.23", - "@visactor/vrender": "1.1.0-alpha.2", - "@visactor/vrender-core": "1.1.0-alpha.2", - "@visactor/vrender-kits": "1.1.0-alpha.2", - "@visactor/vrender-components": "1.1.0-alpha.2", - "@visactor/vrender-animate": "1.1.0-alpha.2", + "@visactor/vrender": "1.1.0-alpha.8", + "@visactor/vrender-core": "1.1.0-alpha.8", + "@visactor/vrender-kits": "1.1.0-alpha.8", + "@visactor/vrender-components": "1.1.0-alpha.8", + "@visactor/vrender-animate": "1.1.0-alpha.8", "@visactor/vutils-extension": "workspace:2.0.22" }, "publishConfig": { diff --git a/tools/story-player/package.json b/tools/story-player/package.json index 6339cca92c..64bea90749 100644 --- a/tools/story-player/package.json +++ b/tools/story-player/package.json @@ -56,10 +56,10 @@ "vite": "3.2.6" }, "dependencies": { - "@visactor/vrender-core": "1.1.0-alpha.2", - "@visactor/vrender-kits": "1.1.0-alpha.2", + "@visactor/vrender-core": "1.1.0-alpha.8", + "@visactor/vrender-kits": "1.1.0-alpha.8", "@visactor/vchart": "workspace:2.0.22", - "@visactor/vrender": "1.1.0-alpha.2", + "@visactor/vrender": "1.1.0-alpha.8", "@visactor/vutils": "~1.0.23" } } From 6bc51096f693f7460b10320b4e817af385a7c3a3 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Tue, 5 May 2026 16:34:34 +0800 Subject: [PATCH 4/7] test: cover VChart animation static truth with manual ticker Add deterministic regression coverage for VRender alpha animation behavior. It exercises VChart with ManualTicker instead of browser timing. Constraint: Uses npm VRender through VChart exports. Confidence: high Scope-risk: narrow Tested: eslint touched animation test --quiet Tested: jest manual-ticker.test.ts --runInBand --silent=false Tested: rush test --only @visactor/vchart Tested: rush install --check-only Tested: rush build --to @visactor/vchart --- .../unit/animation/manual-ticker.test.ts | 403 ++++++++++++++++++ 1 file changed, 403 insertions(+) create mode 100644 packages/vchart/__tests__/unit/animation/manual-ticker.test.ts diff --git a/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts b/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts new file mode 100644 index 0000000000..fe9b5662b0 --- /dev/null +++ b/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts @@ -0,0 +1,403 @@ +import VChart, { + ManualTicker, + type IBarChartSpec, + type IChart, + type IMark, + type IMarkGraphic, + type ISeries +} from '../../../src'; +import { createDiv, removeDom } from '../../util/dom'; + +type AnimatedGraphic = IMarkGraphic & { + attribute: Record; + baseAttributes?: Record; + finalAttribute?: Record; + getFinalAttribute?: () => Record | undefined; +}; + +type TraversableGraphic = AnimatedGraphic & { + type?: string; + name?: string; + parent?: TraversableGraphic; + forEachChildren?: (cb: (child: TraversableGraphic) => void | boolean) => void; +}; + +const APPEAR_DURATION = 300; +const UPDATE_DURATION = 300; +const COLOR_BY_SERIES: Record = { + 'east-利润profit': '#8D72F6', + 'east-销售量sales': '#5766EC', + 'north of east-利润profit': '#66A3FE', + 'north of east-销售量sales': '#51D5E6' +}; + +const createChartContainer = () => { + const container = createDiv(); + const dom = createDiv(container); + + container.style.position = 'fixed'; + container.style.width = '500px'; + container.style.height = '500px'; + container.style.top = '0px'; + container.style.left = '0px'; + + return { container, dom }; +}; + +const createManualTicker = () => { + const ticker = new ManualTicker(); + + ticker.autoStop = false; + + return ticker; +}; + +const getGraphicFinalAttribute = (graphic: AnimatedGraphic) => + graphic.finalAttribute ?? graphic.getFinalAttribute?.() ?? {}; + +const expectClose = (actual: number, expected: number) => { + expect(actual).toBeCloseTo(expected, 6); +}; + +const expectBarYLayout = (graphic: AnimatedGraphic, expected: { y: number; y1: number }) => { + expectClose(graphic.attribute.y, expected.y); + expectClose(graphic.attribute.y1, expected.y1); + expectClose(graphic.baseAttributes?.y, expected.y); + expectClose(graphic.baseAttributes?.y1, expected.y1); + expectClose(getGraphicFinalAttribute(graphic).y, expected.y); + expectClose(getGraphicFinalAttribute(graphic).y1, expected.y1); +}; + +const getBarGraphics = (chart: VChart) => { + const model = chart.getChart() as IChart; + const barSeries = model.getAllSeries().find((series: ISeries) => series.type === 'bar'); + + expect(barSeries).toBeDefined(); + if (!barSeries) { + throw new Error('Expected bar series to exist'); + } + + const barMark = barSeries.getMarks().find((mark: IMark) => mark.name === 'bar'); + + expect(barMark).toBeDefined(); + if (!barMark) { + throw new Error('Expected bar mark to exist'); + } + + return barMark.getGraphics() as AnimatedGraphic[]; +}; + +const walkGraphics = (root: TraversableGraphic, visitor: (graphic: TraversableGraphic) => void) => { + visitor(root); + root.forEachChildren?.((child: TraversableGraphic) => { + walkGraphics(child, visitor); + }); +}; + +const getAnimatedLabelTexts = (chart: VChart) => { + const texts: TraversableGraphic[] = []; + + walkGraphics(chart.getStage() as unknown as TraversableGraphic, graphic => { + if (graphic.type === 'text' && graphic.baseAttributes && getGraphicFinalAttribute(graphic).x !== undefined) { + texts.push(graphic); + } + }); + + return texts; +}; + +const getLabelTextByFill = (chart: VChart, fill: string) => { + const label = getAnimatedLabelTexts(chart).find(graphic => graphic.attribute.fill === fill); + + expect(label).toBeDefined(); + if (!label) { + throw new Error(`Expected label with fill ${fill} to exist`); + } + + return label; +}; + +const getVisibleBarByFill = (chart: VChart, fill: string) => { + const bar = getBarGraphics(chart).find( + graphic => graphic.attribute.fill === fill && graphic.attribute.visible !== false + ); + + expect(bar).toBeDefined(); + if (!bar) { + throw new Error(`Expected visible bar with fill ${fill} to exist`); + } + + return bar; +}; + +const getBarCenterX = (graphic: AnimatedGraphic) => { + const attrs = getGraphicFinalAttribute(graphic); + + return attrs.x + attrs.width / 2; +}; + +const expectStaticXLayout = (graphic: AnimatedGraphic, expectedX: number) => { + expectClose(graphic.baseAttributes?.x, expectedX); + expectClose(getGraphicFinalAttribute(graphic).x, expectedX); +}; + +describe('manual ticker animation regressions', () => { + it('keeps default bar appear starts out of static truth', () => { + const { container, dom } = createChartContainer(); + const ticker = createManualTicker(); + const chart = new VChart( + { + type: 'bar', + width: 400, + height: 300, + data: [ + { + id: 'barData', + values: [{ month: 'Monday', sales: 22 }] + } + ], + xField: 'month', + yField: 'sales', + axes: [ + { orient: 'left', visible: false }, + { orient: 'bottom', visible: false } + ], + animationAppear: { + duration: APPEAR_DURATION, + easing: 'linear' + } + } as IBarChartSpec, + { + dom, + ticker, + animation: true + } + ); + + chart.renderSync(); + + try { + const barGraphic = getBarGraphics(chart)[0]; + const expectedFinalLayout = { + y: getGraphicFinalAttribute(barGraphic).y, + y1: getGraphicFinalAttribute(barGraphic).y1 + }; + + expect(expectedFinalLayout.y).not.toBe(expectedFinalLayout.y1); + expect(barGraphic.baseAttributes?.y).toBe(expectedFinalLayout.y); + expect(barGraphic.baseAttributes?.y1).toBe(expectedFinalLayout.y1); + + ticker.tickAt(APPEAR_DURATION / 2); + + expect(barGraphic.attribute.y).not.toBe(barGraphic.attribute.y1); + expect(barGraphic.baseAttributes?.y).toBe(expectedFinalLayout.y); + expect(barGraphic.baseAttributes?.y1).toBe(expectedFinalLayout.y1); + expect(getGraphicFinalAttribute(barGraphic).y).toBe(expectedFinalLayout.y); + expect(getGraphicFinalAttribute(barGraphic).y1).toBe(expectedFinalLayout.y1); + + ticker.tickAt(APPEAR_DURATION + 50); + + expectBarYLayout(barGraphic, expectedFinalLayout); + expect(barGraphic.attribute.y).not.toBe(barGraphic.attribute.y1); + } finally { + chart.release(); + ticker.release(); + removeDom(container); + } + }); + + it('keeps data label update final x at the filtered bar position', () => { + const { container, dom } = createChartContainer(); + const ticker = createManualTicker(); + const chart = new VChart( + { + type: 'bar', + direction: 'vertical', + width: 500, + height: 300, + xField: ['date', '__DimGroup__'], + yField: '__MeaValue__', + seriesField: '__DimGroupID__', + padding: 0, + region: [ + { + clip: true + } + ], + animation: true, + animationAppear: { + duration: APPEAR_DURATION, + easing: 'linear' + }, + animationUpdate: { + duration: UPDATE_DURATION, + easing: 'linear' + }, + stackCornerRadius: [4, 4, 0, 0], + color: { + type: 'ordinal', + domain: Object.keys(COLOR_BY_SERIES), + range: Object.values(COLOR_BY_SERIES), + specified: {} + }, + data: { + values: [ + { + date: '2019', + region: 'east', + __OriginalData__: { + date: '2019', + region: 'east', + profit: 10, + sales: 20 + }, + profit: 10, + __MeaId__: 'profit', + __MeaName__: '利润', + __MeaValue__: 10, + __DimGroup__: 'east-利润', + __DimGroupID__: 'east-利润profit' + }, + { + date: '2019', + region: 'east', + __OriginalData__: { + date: '2019', + region: 'east', + profit: 10, + sales: 20 + }, + sales: 20, + __MeaId__: 'sales', + __MeaName__: '销售量', + __MeaValue__: 20, + __DimGroup__: 'east-销售量', + __DimGroupID__: 'east-销售量sales' + }, + { + date: '2019', + region: 'north of east', + __OriginalData__: { + date: '2019', + region: 'north of east', + profit: 10, + sales: 20 + }, + profit: 10, + __MeaId__: 'profit', + __MeaName__: '利润', + __MeaValue__: 10, + __DimGroup__: 'north of east-利润', + __DimGroupID__: 'north of east-利润profit' + }, + { + date: '2019', + region: 'north of east', + __OriginalData__: { + date: '2019', + region: 'north of east', + profit: 10, + sales: 20 + }, + sales: 20, + __MeaId__: 'sales', + __MeaName__: '销售量', + __MeaValue__: 20, + __DimGroup__: 'north of east-销售量', + __DimGroupID__: 'north of east-销售量sales' + } + ] + }, + label: { + visible: true, + animationUpdate: { + duration: UPDATE_DURATION, + easing: 'linear' + } + }, + legends: { + type: 'discrete', + visible: true, + maxCol: 1, + maxRow: 1, + autoPage: true, + orient: 'right', + position: 'start', + item: { + focus: true, + background: { + state: { + selectedHover: { + fill: '#646A73', + fillOpacity: 0.05 + } + } + } + } + } + } as unknown as IBarChartSpec, + { + dom, + ticker, + animation: true + } + ); + + chart.renderSync(); + + try { + ticker.tickAt(APPEAR_DURATION + 50); + + const retainedSeries = Object.keys(COLOR_BY_SERIES).slice(0, 3); + const initialXBySeries = retainedSeries.reduce>((result, seriesName) => { + result[seriesName] = getLabelTextByFill(chart, COLOR_BY_SERIES[seriesName]).attribute.x; + return result; + }, {}); + + chart.setLegendSelectedDataByIndex(0, retainedSeries); + chart.renderSync(); + + const updateStart = ticker.getTime(); + const expectedXBySeries = retainedSeries.reduce>((result, seriesName) => { + result[seriesName] = getBarCenterX(getVisibleBarByFill(chart, COLOR_BY_SERIES[seriesName])); + return result; + }, {}); + + expect(retainedSeries.some(seriesName => initialXBySeries[seriesName] !== expectedXBySeries[seriesName])).toBe( + true + ); + + retainedSeries.forEach(seriesName => { + expectStaticXLayout(getLabelTextByFill(chart, COLOR_BY_SERIES[seriesName]), expectedXBySeries[seriesName]); + }); + + ticker.tickAt(updateStart + UPDATE_DURATION / 2); + + retainedSeries.forEach(seriesName => { + const label = getLabelTextByFill(chart, COLOR_BY_SERIES[seriesName]); + const startX = initialXBySeries[seriesName]; + const finalX = expectedXBySeries[seriesName]; + + if (startX !== finalX) { + expect(label.attribute.x).toBeGreaterThanOrEqual(Math.min(startX, finalX)); + expect(label.attribute.x).toBeLessThanOrEqual(Math.max(startX, finalX)); + } + expectStaticXLayout(label, finalX); + }); + + ticker.tickAt(updateStart + UPDATE_DURATION + 50); + + retainedSeries.forEach(seriesName => { + const label = getLabelTextByFill(chart, COLOR_BY_SERIES[seriesName]); + const finalX = expectedXBySeries[seriesName]; + + expectClose(label.attribute.x, finalX); + expectStaticXLayout(label, finalX); + }); + } finally { + chart.release(); + ticker.release(); + removeDom(container); + } + }); +}); From 5ba9f2c7860641ce46059ca6d768b7b294c0f9bd Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Wed, 6 May 2026 14:30:31 +0800 Subject: [PATCH 5/7] fix: keep miniapp builds from resolving shadow-root fallback VRender alpha leaves a shadow-root fallback require inside the generated VChart miniapp bundle. The lark and wx second-pass Rollup builds should leave that fallback untouched instead of converting it into an unresolved proxy module. Constraint: Do not patch VRender or generated miniapp bundles. Rejected: Commit regenerated miniapp bundles | generated outputs are not source. Confidence: high Scope-risk: narrow Tested: node common/scripts/install-run-rush.js build --to @visactor/vchart Tested: node common/scripts/install-run-rush.js build --only @visactor/lark-vchart Tested: node common/scripts/install-run-rush.js build --only @visactor/wx-vchart Tested: eslint lark/wx rollup configs --quiet Tested: node common/scripts/install-run-rush.js install --check-only --- packages/lark-vchart/rollup.config.ts | 3 ++- packages/wx-vchart/rollup.config.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/lark-vchart/rollup.config.ts b/packages/lark-vchart/rollup.config.ts index 315e9985b7..97be5ae637 100644 --- a/packages/lark-vchart/rollup.config.ts +++ b/packages/lark-vchart/rollup.config.ts @@ -18,7 +18,8 @@ let options = defineConfig([ plugins: [ resolve(), commonjs({ - include: './src/vchart/index.js' + include: './src/vchart/index.js', + ignore: ['./shadow-root'] }), babel({ presets: [['@babel/preset-env']], diff --git a/packages/wx-vchart/rollup.config.ts b/packages/wx-vchart/rollup.config.ts index f76310a3b8..a91b225958 100644 --- a/packages/wx-vchart/rollup.config.ts +++ b/packages/wx-vchart/rollup.config.ts @@ -18,7 +18,8 @@ let options = defineConfig([ plugins: [ resolve(), commonjs({ - include: './miniprogram/src/vchart/index.js' + include: './miniprogram/src/vchart/index.js', + ignore: ['./shadow-root'] }), babel({ // presets: [['@babel/preset-env']], From db138714da0975f0f8724b71113bd453fb3e40d7 Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Wed, 6 May 2026 17:31:10 +0800 Subject: [PATCH 6/7] chore: validate VRender alpha.9 animation fix Consume the npm-published VRender alpha.9 packages and lock the stackCornerRadius repeated legend filtering regression with a manual ticker test. The test interrupts update animations and checks final bar plus clip-path static truth. Constraint: Registry packages only; no local VRender link or pack. Rejected: Browser-only manual repro | timing regressions need deterministic checks. Confidence: high Scope-risk: moderate Directive: Keep this regression on manual ticker timing. Tested: npm test -- --runInBand __tests__/unit/animation/manual-ticker.test.ts Tested: node common/scripts/install-run-rush.js install --check-only Tested: eslint packages/vchart/__tests__/unit/animation/manual-ticker.test.ts --quiet Tested: node common/scripts/install-run-rush.js test --only @visactor/vchart Tested: node common/scripts/install-run-rush.js build --to @visactor/vchart Not-tested: Full browser visual smoke in this turn. --- common/config/rush/pnpm-lock.yaml | 120 ++++----- docs/package.json | 4 +- packages/openinula-vchart/package.json | 4 +- packages/react-vchart/package.json | 4 +- packages/vchart-extension/package.json | 8 +- .../unit/animation/manual-ticker.test.ts | 247 ++++++++++++++++++ packages/vchart/package.json | 10 +- tools/story-player/package.json | 6 +- 8 files changed, 325 insertions(+), 78 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index acd53bb92f..15def21eab 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -37,11 +37,11 @@ importers: specifier: 1.2.4-alpha.5 version: 1.2.4-alpha.5 '@visactor/vrender': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vtable': specifier: 1.19.0-alpha.0 version: 1.19.0-alpha.0 @@ -203,11 +203,11 @@ importers: specifier: workspace:2.0.22 version: link:../vchart '@visactor/vrender-core': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -294,11 +294,11 @@ importers: specifier: workspace:2.0.22 version: link:../vchart-extension '@visactor/vrender-core': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -529,20 +529,20 @@ importers: specifier: ~1.0.23 version: 1.0.23 '@visactor/vrender': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-animate': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-components': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-core': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vscale': specifier: ~1.0.23 version: 1.0.23 @@ -695,17 +695,17 @@ importers: specifier: ~1.0.23 version: 1.0.23 '@visactor/vrender-animate': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-components': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-core': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -1263,14 +1263,14 @@ importers: specifier: workspace:2.0.22 version: link:../../packages/vchart '@visactor/vrender': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-core': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.8 - version: 1.1.0-alpha.8 + specifier: 1.1.0-alpha.9 + version: 1.1.0-alpha.9 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -3076,29 +3076,29 @@ packages: '@visactor/vrender-animate@1.0.0-alpha.18': resolution: {integrity: sha512-9kTtvp1ef+1t+AtUiza6A7qBQP7SmvOu3/ILGrqs/HGdZVj1XGjbYvD/X/zwKJ3LEb7gGV5fa8x95e4czTvRSA==} - '@visactor/vrender-animate@1.1.0-alpha.8': - resolution: {integrity: sha512-T1CxxlF2QXJ9ElfzwV2tidNW2BZrU2kkk4vCEs7tAZbk4blQvFzUKosU+rZnIUXvmUQ1SLcjpGb6nwiJ35qL9Q==} + '@visactor/vrender-animate@1.1.0-alpha.9': + resolution: {integrity: sha512-dVWOYQeZmttKLBlkl38aDSXzcz8Oqygc8M7RKSCP+jsb0s2kns9jaiaGaK2J1uEEtLxJ4Itz3CGedB+QaqtF9Q==} '@visactor/vrender-components@1.0.0-alpha.18': resolution: {integrity: sha512-7Euq+ZfswL74n2pgkaqZSsPxoSa5SPIGyXatN1eUrdzM2Z0kX6U0RcJg01fctvRs4op6WhcecRLqGvnHcBeb9Q==} - '@visactor/vrender-components@1.1.0-alpha.8': - resolution: {integrity: sha512-UgiULMccv93ta4nI3Ee6LO8+LOpiZNDhXAM/xvFbIx9FccpOYWxdQKzH2B7aGzx1HlINz7Pp2+2QWi6+MedPHA==} + '@visactor/vrender-components@1.1.0-alpha.9': + resolution: {integrity: sha512-4PMPlLiy+Eyo9+Zhx2vKOw6WvpDQYWUaSrr4gRRKUaTmhEtJk7YNHlrhO37YBvv6iHLhKdhIQR23GvFL7AJ/Gg==} '@visactor/vrender-core@1.0.0-alpha.18': resolution: {integrity: sha512-0ihtNvCyNkOsWPFgRqowHzq0IcQgS2Wl/nPpKbVtxWKveenwlhA+ZKoQvam6VJyBY7jeNe1pROy0mJMDyVAJQw==} - '@visactor/vrender-core@1.1.0-alpha.8': - resolution: {integrity: sha512-Wvvr+G9MVCkigaYoK1a7Y29AphgunA9cgGquetMF8Bch9emMv6KeFer2hUgSCRxD90MV53OxNHKtpIKVVSOPkw==} + '@visactor/vrender-core@1.1.0-alpha.9': + resolution: {integrity: sha512-qs7iA2BKFWWQILTkWvLYjcjror2+oXMKsVE35UoqTSHMXgzqk7Em1oGAFgukEKRcYRqKMop6oNfUG8DbvH5dcg==} '@visactor/vrender-kits@1.0.0-alpha.18': resolution: {integrity: sha512-Tvolkq+4G8qiPFZo0Aj8M//Yr6jR2h8FNkFEyWM9gbQbEiTkjpmHAJOYnoSsaPtPrcMSlG4EhJSFDk6ymANHVg==} - '@visactor/vrender-kits@1.1.0-alpha.8': - resolution: {integrity: sha512-jBUL5UMlEArtjrbHs0D+cNmr2sNgDpDTVcWNRg43JeK4JZTL+WySx5UkAjkUY03N0arskX96O6g5sQwZG9u5tQ==} + '@visactor/vrender-kits@1.1.0-alpha.9': + resolution: {integrity: sha512-dACr5rnEkzncekbfraYqcDK9LVYxcUnMN/73OSwrpeog2dbwFDZ/iBoET724NecpmGmi3WAEE+pINB6/acBgvA==} - '@visactor/vrender@1.1.0-alpha.8': - resolution: {integrity: sha512-Bn9T9T3yFqpvSS7rVTqvL59A6XPsGDY5TNqJ8K1Mb1GCASfv5YS70NTFmQipvWHrebatWyodKlEEradbflMegA==} + '@visactor/vrender@1.1.0-alpha.9': + resolution: {integrity: sha512-vaUTjFPwEsZS3iIrYY5VY5bCMa45hWT5JlO1OP9daKMxN+3nU67bRRCAT1vbOTG69RCcYyqarFsTMl3wS8BqEQ==} '@visactor/vscale@0.18.18': resolution: {integrity: sha512-iRG4kv+5Fv4KX3AxEfV95XU3I6OmF0QizyAhqHxKa7L1MaT+MRvDDk5zHWf1E8gialLbL2xDe3GnT6g/4u5jhA==} @@ -15029,9 +15029,9 @@ snapshots: '@visactor/vrender-core': 1.0.0-alpha.18 '@visactor/vutils': 1.0.4 - '@visactor/vrender-animate@1.1.0-alpha.8': + '@visactor/vrender-animate@1.1.0-alpha.9': dependencies: - '@visactor/vrender-core': 1.1.0-alpha.8 + '@visactor/vrender-core': 1.1.0-alpha.9 '@visactor/vutils': 1.0.23 '@visactor/vrender-components@1.0.0-alpha.18': @@ -15042,11 +15042,11 @@ snapshots: '@visactor/vscale': 1.0.4 '@visactor/vutils': 1.0.4 - '@visactor/vrender-components@1.1.0-alpha.8': + '@visactor/vrender-components@1.1.0-alpha.9': dependencies: - '@visactor/vrender-animate': 1.1.0-alpha.8 - '@visactor/vrender-core': 1.1.0-alpha.8 - '@visactor/vrender-kits': 1.1.0-alpha.8 + '@visactor/vrender-animate': 1.1.0-alpha.9 + '@visactor/vrender-core': 1.1.0-alpha.9 + '@visactor/vrender-kits': 1.1.0-alpha.9 '@visactor/vscale': 1.0.23 '@visactor/vutils': 1.0.23 @@ -15055,7 +15055,7 @@ snapshots: '@visactor/vutils': 1.0.4 color-convert: 2.0.1 - '@visactor/vrender-core@1.1.0-alpha.8': + '@visactor/vrender-core@1.1.0-alpha.9': dependencies: '@visactor/vutils': 1.0.23 color-convert: 2.0.1 @@ -15069,21 +15069,21 @@ snapshots: lottie-web: 5.13.0 roughjs: 4.5.2 - '@visactor/vrender-kits@1.1.0-alpha.8': + '@visactor/vrender-kits@1.1.0-alpha.9': dependencies: '@resvg/resvg-js': 2.4.1 - '@visactor/vrender-core': 1.1.0-alpha.8 + '@visactor/vrender-core': 1.1.0-alpha.9 '@visactor/vutils': 1.0.23 gifuct-js: 2.1.2 lottie-web: 5.13.0 roughjs: 4.6.6 - '@visactor/vrender@1.1.0-alpha.8': + '@visactor/vrender@1.1.0-alpha.9': dependencies: - '@visactor/vrender-animate': 1.1.0-alpha.8 - '@visactor/vrender-components': 1.1.0-alpha.8 - '@visactor/vrender-core': 1.1.0-alpha.8 - '@visactor/vrender-kits': 1.1.0-alpha.8 + '@visactor/vrender-animate': 1.1.0-alpha.9 + '@visactor/vrender-components': 1.1.0-alpha.9 + '@visactor/vrender-core': 1.1.0-alpha.9 + '@visactor/vrender-kits': 1.1.0-alpha.9 '@visactor/vscale@0.18.18': dependencies: diff --git a/docs/package.json b/docs/package.json index 98e9ce617c..d8e19498e3 100644 --- a/docs/package.json +++ b/docs/package.json @@ -19,8 +19,8 @@ "@visactor/vchart-theme": "~1.6.6", "@visactor/vmind": "1.2.4-alpha.5", "@visactor/vutils": "~1.0.23", - "@visactor/vrender": "1.1.0-alpha.8", - "@visactor/vrender-kits": "1.1.0-alpha.8", + "@visactor/vrender": "1.1.0-alpha.9", + "@visactor/vrender-kits": "1.1.0-alpha.9", "@visactor/vtable": "1.19.0-alpha.0", "@visactor/vtable-editors": "1.19.0-alpha.0", "@visactor/vtable-gantt": "1.19.0-alpha.0", diff --git a/packages/openinula-vchart/package.json b/packages/openinula-vchart/package.json index 4a6f62d2a5..cf107f14f2 100644 --- a/packages/openinula-vchart/package.json +++ b/packages/openinula-vchart/package.json @@ -30,8 +30,8 @@ "dependencies": { "@visactor/vchart": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", - "@visactor/vrender-core": "1.1.0-alpha.8", - "@visactor/vrender-kits": "1.1.0-alpha.8", + "@visactor/vrender-core": "1.1.0-alpha.9", + "@visactor/vrender-kits": "1.1.0-alpha.9", "react-is": "^18.2.0" }, "devDependencies": { diff --git a/packages/react-vchart/package.json b/packages/react-vchart/package.json index c9afe14ab1..9f362934ab 100644 --- a/packages/react-vchart/package.json +++ b/packages/react-vchart/package.json @@ -36,8 +36,8 @@ "@visactor/vchart": "workspace:2.0.22", "@visactor/vchart-extension": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", - "@visactor/vrender-core": "1.1.0-alpha.8", - "@visactor/vrender-kits": "1.1.0-alpha.8", + "@visactor/vrender-core": "1.1.0-alpha.9", + "@visactor/vrender-kits": "1.1.0-alpha.9", "react-is": "^18.2.0" }, "devDependencies": { diff --git a/packages/vchart-extension/package.json b/packages/vchart-extension/package.json index 5136ef67d1..994627a02c 100644 --- a/packages/vchart-extension/package.json +++ b/packages/vchart-extension/package.json @@ -26,10 +26,10 @@ "start": "ts-node __tests__/runtime/browser/scripts/initVite.ts && vite serve __tests__/runtime/browser" }, "dependencies": { - "@visactor/vrender-core": "1.1.0-alpha.8", - "@visactor/vrender-kits": "1.1.0-alpha.8", - "@visactor/vrender-components": "1.1.0-alpha.8", - "@visactor/vrender-animate": "1.1.0-alpha.8", + "@visactor/vrender-core": "1.1.0-alpha.9", + "@visactor/vrender-kits": "1.1.0-alpha.9", + "@visactor/vrender-components": "1.1.0-alpha.9", + "@visactor/vrender-animate": "1.1.0-alpha.9", "@visactor/vchart": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", "@visactor/vdataset": "~1.0.23", diff --git a/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts b/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts index fe9b5662b0..20f0745176 100644 --- a/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts +++ b/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts @@ -141,6 +141,178 @@ const expectStaticXLayout = (graphic: AnimatedGraphic, expectedX: number) => { expectClose(getGraphicFinalAttribute(graphic).x, expectedX); }; +const expectStaticRectLayout = ( + graphic: AnimatedGraphic, + expected: { x: number; width: number; y: number; y1: number } +) => { + expectClose(graphic.attribute.x, expected.x); + expectClose(graphic.attribute.width, expected.width); + expectClose(graphic.attribute.y, expected.y); + expectClose(graphic.attribute.y1, expected.y1); + expectClose(graphic.baseAttributes?.x, expected.x); + expectClose(graphic.baseAttributes?.width, expected.width); + expectClose(graphic.baseAttributes?.y, expected.y); + expectClose(graphic.baseAttributes?.y1, expected.y1); + expectClose(getGraphicFinalAttribute(graphic).x, expected.x); + expectClose(getGraphicFinalAttribute(graphic).width, expected.width); + expectClose(getGraphicFinalAttribute(graphic).y, expected.y); + expectClose(getGraphicFinalAttribute(graphic).y1, expected.y1); +}; + +const getBarMarkProduct = (chart: VChart) => { + const model = chart.getChart() as IChart; + const barSeries = model.getAllSeries().find((series: ISeries) => series.type === 'bar'); + + expect(barSeries).toBeDefined(); + if (!barSeries) { + throw new Error('Expected bar series to exist'); + } + + const barMark = barSeries.getMarks().find((mark: IMark) => mark.name === 'bar') as IMark & { + getProduct?: () => AnimatedGraphic; + }; + + expect(barMark).toBeDefined(); + if (!barMark?.getProduct) { + throw new Error('Expected bar mark product to exist'); + } + + return barMark.getProduct(); +}; + +const getBarClipPathRects = (chart: VChart) => + ((getBarMarkProduct(chart).attribute.path ?? []) as AnimatedGraphic[]).map( + path => path.baseAttributes ?? path.attribute + ); + +const createStackCornerLegendSpec = () => + ({ + type: 'bar', + direction: 'vertical', + width: 500, + height: 300, + xField: ['date', '__DimGroup__'], + yField: '__MeaValue__', + seriesField: '__DimGroupID__', + padding: 0, + region: [ + { + clip: true + } + ], + animation: true, + animationAppear: { + duration: APPEAR_DURATION, + easing: 'linear' + }, + animationUpdate: { + duration: UPDATE_DURATION, + easing: 'linear' + }, + stackCornerRadius: [4, 4, 0, 0], + color: { + type: 'ordinal', + domain: Object.keys(COLOR_BY_SERIES), + range: Object.values(COLOR_BY_SERIES), + specified: {} + }, + data: { + values: [ + { + date: '2019', + region: 'east', + __OriginalData__: { + date: '2019', + region: 'east', + profit: 10, + sales: 20 + }, + profit: 10, + __MeaId__: 'profit', + __MeaName__: '利润', + __MeaValue__: 10, + __DimGroup__: 'east-利润', + __DimGroupID__: 'east-利润profit' + }, + { + date: '2019', + region: 'east', + __OriginalData__: { + date: '2019', + region: 'east', + profit: 10, + sales: 20 + }, + sales: 20, + __MeaId__: 'sales', + __MeaName__: '销售量', + __MeaValue__: 20, + __DimGroup__: 'east-销售量', + __DimGroupID__: 'east-销售量sales' + }, + { + date: '2019', + region: 'north of east', + __OriginalData__: { + date: '2019', + region: 'north of east', + profit: 10, + sales: 20 + }, + profit: 10, + __MeaId__: 'profit', + __MeaName__: '利润', + __MeaValue__: 10, + __DimGroup__: 'north of east-利润', + __DimGroupID__: 'north of east-利润profit' + }, + { + date: '2019', + region: 'north of east', + __OriginalData__: { + date: '2019', + region: 'north of east', + profit: 10, + sales: 20 + }, + sales: 20, + __MeaId__: 'sales', + __MeaName__: '销售量', + __MeaValue__: 20, + __DimGroup__: 'north of east-销售量', + __DimGroupID__: 'north of east-销售量sales' + } + ] + }, + label: { + visible: true, + animationUpdate: { + duration: UPDATE_DURATION, + easing: 'linear' + } + }, + legends: { + type: 'discrete', + visible: true, + maxCol: 1, + maxRow: 1, + autoPage: true, + orient: 'right', + position: 'start', + item: { + focus: true, + background: { + state: { + selectedHover: { + fill: '#646A73', + fillOpacity: 0.05 + } + } + } + } + } + } as unknown as IBarChartSpec); + describe('manual ticker animation regressions', () => { it('keeps default bar appear starts out of static truth', () => { const { container, dom } = createChartContainer(); @@ -400,4 +572,79 @@ describe('manual ticker animation regressions', () => { removeDom(container); } }); + + it('keeps stack corner clip paths aligned after interrupted legend update animations', () => { + const { container, dom } = createChartContainer(); + const ticker = createManualTicker(); + const chart = new VChart(createStackCornerLegendSpec(), { + dom, + ticker, + animation: true + }); + + chart.renderSync(); + + try { + ticker.tickAt(APPEAR_DURATION + 50); + + const allSeries = Object.keys(COLOR_BY_SERIES); + const retainedSeries = allSeries.slice(0, 3); + + chart.setLegendSelectedDataByIndex(0, retainedSeries); + chart.renderSync(); + + const firstUpdate = ticker.getTime(); + + ticker.tickAt(firstUpdate + UPDATE_DURATION / 2); + + chart.setLegendSelectedDataByIndex(0, allSeries); + chart.renderSync(); + + const secondUpdate = ticker.getTime(); + + ticker.tickAt(secondUpdate + UPDATE_DURATION / 2); + + chart.setLegendSelectedDataByIndex(0, retainedSeries); + chart.renderSync(); + + const thirdUpdate = ticker.getTime(); + const expectedRects = retainedSeries.map(seriesName => { + const graphic = getVisibleBarByFill(chart, COLOR_BY_SERIES[seriesName]); + const finalAttribute = getGraphicFinalAttribute(graphic); + + return { + x: finalAttribute.x, + width: finalAttribute.width, + y: finalAttribute.y, + y1: finalAttribute.y1 + }; + }); + const clipPathRects = getBarClipPathRects(chart); + + expect(clipPathRects.length).toBe(retainedSeries.length); + clipPathRects.forEach((clipPathRect, index) => { + expectClose(clipPathRect.x, expectedRects[index].x); + expectClose(clipPathRect.width, expectedRects[index].width); + expectClose(clipPathRect.y, expectedRects[index].y); + expectClose(clipPathRect.y1, expectedRects[index].y1); + }); + + ticker.tickAt(thirdUpdate + UPDATE_DURATION + 50); + + const finalClipPathRects = getBarClipPathRects(chart); + + expect(finalClipPathRects.length).toBe(retainedSeries.length); + retainedSeries.forEach((seriesName, index) => { + expectStaticRectLayout(getVisibleBarByFill(chart, COLOR_BY_SERIES[seriesName]), expectedRects[index]); + expectClose(finalClipPathRects[index].x, expectedRects[index].x); + expectClose(finalClipPathRects[index].width, expectedRects[index].width); + expectClose(finalClipPathRects[index].y, expectedRects[index].y); + expectClose(finalClipPathRects[index].y1, expectedRects[index].y1); + }); + } finally { + chart.release(); + ticker.release(); + removeDom(container); + } + }); }); diff --git a/packages/vchart/package.json b/packages/vchart/package.json index 6052a4dc7f..5667cd6aeb 100644 --- a/packages/vchart/package.json +++ b/packages/vchart/package.json @@ -126,11 +126,11 @@ "@visactor/vdataset": "~1.0.23", "@visactor/vscale": "~1.0.23", "@visactor/vlayouts": "~1.0.23", - "@visactor/vrender": "1.1.0-alpha.8", - "@visactor/vrender-core": "1.1.0-alpha.8", - "@visactor/vrender-kits": "1.1.0-alpha.8", - "@visactor/vrender-components": "1.1.0-alpha.8", - "@visactor/vrender-animate": "1.1.0-alpha.8", + "@visactor/vrender": "1.1.0-alpha.9", + "@visactor/vrender-core": "1.1.0-alpha.9", + "@visactor/vrender-kits": "1.1.0-alpha.9", + "@visactor/vrender-components": "1.1.0-alpha.9", + "@visactor/vrender-animate": "1.1.0-alpha.9", "@visactor/vutils-extension": "workspace:2.0.22" }, "publishConfig": { diff --git a/tools/story-player/package.json b/tools/story-player/package.json index 64bea90749..69247dd3b7 100644 --- a/tools/story-player/package.json +++ b/tools/story-player/package.json @@ -56,10 +56,10 @@ "vite": "3.2.6" }, "dependencies": { - "@visactor/vrender-core": "1.1.0-alpha.8", - "@visactor/vrender-kits": "1.1.0-alpha.8", + "@visactor/vrender-core": "1.1.0-alpha.9", + "@visactor/vrender-kits": "1.1.0-alpha.9", "@visactor/vchart": "workspace:2.0.22", - "@visactor/vrender": "1.1.0-alpha.8", + "@visactor/vrender": "1.1.0-alpha.9", "@visactor/vutils": "~1.0.23" } } From 645cbef2244f4e06f8adcee083e13e493b94e89a Mon Sep 17 00:00:00 2001 From: "lixuefei.1313" Date: Wed, 6 May 2026 20:17:59 +0800 Subject: [PATCH 7/7] feat: upgrade vrender to fix clipPath --- common/config/rush/pnpm-lock.yaml | 120 +++++++++--------- docs/package.json | 4 +- packages/openinula-vchart/package.json | 4 +- packages/react-vchart/package.json | 4 +- packages/vchart-extension/package.json | 8 +- .../unit/animation/manual-ticker.test.ts | 74 +++++++++++ packages/vchart/package.json | 10 +- tools/story-player/package.json | 6 +- 8 files changed, 152 insertions(+), 78 deletions(-) diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 15def21eab..5b8a30cce6 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -37,11 +37,11 @@ importers: specifier: 1.2.4-alpha.5 version: 1.2.4-alpha.5 '@visactor/vrender': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vtable': specifier: 1.19.0-alpha.0 version: 1.19.0-alpha.0 @@ -203,11 +203,11 @@ importers: specifier: workspace:2.0.22 version: link:../vchart '@visactor/vrender-core': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -294,11 +294,11 @@ importers: specifier: workspace:2.0.22 version: link:../vchart-extension '@visactor/vrender-core': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -529,20 +529,20 @@ importers: specifier: ~1.0.23 version: 1.0.23 '@visactor/vrender': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-animate': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-components': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-core': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vscale': specifier: ~1.0.23 version: 1.0.23 @@ -695,17 +695,17 @@ importers: specifier: ~1.0.23 version: 1.0.23 '@visactor/vrender-animate': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-components': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-core': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -1263,14 +1263,14 @@ importers: specifier: workspace:2.0.22 version: link:../../packages/vchart '@visactor/vrender': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-core': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vrender-kits': - specifier: 1.1.0-alpha.9 - version: 1.1.0-alpha.9 + specifier: 1.1.0-alpha.11 + version: 1.1.0-alpha.11 '@visactor/vutils': specifier: ~1.0.23 version: 1.0.23 @@ -3076,29 +3076,29 @@ packages: '@visactor/vrender-animate@1.0.0-alpha.18': resolution: {integrity: sha512-9kTtvp1ef+1t+AtUiza6A7qBQP7SmvOu3/ILGrqs/HGdZVj1XGjbYvD/X/zwKJ3LEb7gGV5fa8x95e4czTvRSA==} - '@visactor/vrender-animate@1.1.0-alpha.9': - resolution: {integrity: sha512-dVWOYQeZmttKLBlkl38aDSXzcz8Oqygc8M7RKSCP+jsb0s2kns9jaiaGaK2J1uEEtLxJ4Itz3CGedB+QaqtF9Q==} + '@visactor/vrender-animate@1.1.0-alpha.11': + resolution: {integrity: sha512-RgWD8BYip6QvrkQsXJWvHvboWoyuvHmycxDY4Pq5NKyWxatns5S4n1kwdeViRtBd8bKklVAc/UE4d3BbGv2zJA==} '@visactor/vrender-components@1.0.0-alpha.18': resolution: {integrity: sha512-7Euq+ZfswL74n2pgkaqZSsPxoSa5SPIGyXatN1eUrdzM2Z0kX6U0RcJg01fctvRs4op6WhcecRLqGvnHcBeb9Q==} - '@visactor/vrender-components@1.1.0-alpha.9': - resolution: {integrity: sha512-4PMPlLiy+Eyo9+Zhx2vKOw6WvpDQYWUaSrr4gRRKUaTmhEtJk7YNHlrhO37YBvv6iHLhKdhIQR23GvFL7AJ/Gg==} + '@visactor/vrender-components@1.1.0-alpha.11': + resolution: {integrity: sha512-yGxjh1vYyxiSQoPLxJYpRgLtcQlpT0k+p2IGKvtU7zt6RGFSDSThpSHnAwHcWkFKy6X/6r8P28YmoYl5hhLWSw==} '@visactor/vrender-core@1.0.0-alpha.18': resolution: {integrity: sha512-0ihtNvCyNkOsWPFgRqowHzq0IcQgS2Wl/nPpKbVtxWKveenwlhA+ZKoQvam6VJyBY7jeNe1pROy0mJMDyVAJQw==} - '@visactor/vrender-core@1.1.0-alpha.9': - resolution: {integrity: sha512-qs7iA2BKFWWQILTkWvLYjcjror2+oXMKsVE35UoqTSHMXgzqk7Em1oGAFgukEKRcYRqKMop6oNfUG8DbvH5dcg==} + '@visactor/vrender-core@1.1.0-alpha.11': + resolution: {integrity: sha512-BGTtnZAmRkPredqFwLnVxoRbb/OLRbVfAjR3gz8cWjrz6QwL8WJT7QgzQX/GvjZcIio6q01ONPejLWLnVmVWrQ==} '@visactor/vrender-kits@1.0.0-alpha.18': resolution: {integrity: sha512-Tvolkq+4G8qiPFZo0Aj8M//Yr6jR2h8FNkFEyWM9gbQbEiTkjpmHAJOYnoSsaPtPrcMSlG4EhJSFDk6ymANHVg==} - '@visactor/vrender-kits@1.1.0-alpha.9': - resolution: {integrity: sha512-dACr5rnEkzncekbfraYqcDK9LVYxcUnMN/73OSwrpeog2dbwFDZ/iBoET724NecpmGmi3WAEE+pINB6/acBgvA==} + '@visactor/vrender-kits@1.1.0-alpha.11': + resolution: {integrity: sha512-n/XGF5equ/7quxKUTFMlWGKxu6znp/sN8i/2rIaQ/HyZDQBj558LX4qhFLqWeWikHENv96CYFUrVhOfQXGeqQA==} - '@visactor/vrender@1.1.0-alpha.9': - resolution: {integrity: sha512-vaUTjFPwEsZS3iIrYY5VY5bCMa45hWT5JlO1OP9daKMxN+3nU67bRRCAT1vbOTG69RCcYyqarFsTMl3wS8BqEQ==} + '@visactor/vrender@1.1.0-alpha.11': + resolution: {integrity: sha512-ilKoCLi2Ax4oUmzCbFNmNdmEqcitk7ESlryGAQPUIO1DVJkmu8H2zzO4gUlunEKYkMhCSOj4a1bI8/7jUMO2eQ==} '@visactor/vscale@0.18.18': resolution: {integrity: sha512-iRG4kv+5Fv4KX3AxEfV95XU3I6OmF0QizyAhqHxKa7L1MaT+MRvDDk5zHWf1E8gialLbL2xDe3GnT6g/4u5jhA==} @@ -15029,9 +15029,9 @@ snapshots: '@visactor/vrender-core': 1.0.0-alpha.18 '@visactor/vutils': 1.0.4 - '@visactor/vrender-animate@1.1.0-alpha.9': + '@visactor/vrender-animate@1.1.0-alpha.11': dependencies: - '@visactor/vrender-core': 1.1.0-alpha.9 + '@visactor/vrender-core': 1.1.0-alpha.11 '@visactor/vutils': 1.0.23 '@visactor/vrender-components@1.0.0-alpha.18': @@ -15042,11 +15042,11 @@ snapshots: '@visactor/vscale': 1.0.4 '@visactor/vutils': 1.0.4 - '@visactor/vrender-components@1.1.0-alpha.9': + '@visactor/vrender-components@1.1.0-alpha.11': dependencies: - '@visactor/vrender-animate': 1.1.0-alpha.9 - '@visactor/vrender-core': 1.1.0-alpha.9 - '@visactor/vrender-kits': 1.1.0-alpha.9 + '@visactor/vrender-animate': 1.1.0-alpha.11 + '@visactor/vrender-core': 1.1.0-alpha.11 + '@visactor/vrender-kits': 1.1.0-alpha.11 '@visactor/vscale': 1.0.23 '@visactor/vutils': 1.0.23 @@ -15055,7 +15055,7 @@ snapshots: '@visactor/vutils': 1.0.4 color-convert: 2.0.1 - '@visactor/vrender-core@1.1.0-alpha.9': + '@visactor/vrender-core@1.1.0-alpha.11': dependencies: '@visactor/vutils': 1.0.23 color-convert: 2.0.1 @@ -15069,21 +15069,21 @@ snapshots: lottie-web: 5.13.0 roughjs: 4.5.2 - '@visactor/vrender-kits@1.1.0-alpha.9': + '@visactor/vrender-kits@1.1.0-alpha.11': dependencies: '@resvg/resvg-js': 2.4.1 - '@visactor/vrender-core': 1.1.0-alpha.9 + '@visactor/vrender-core': 1.1.0-alpha.11 '@visactor/vutils': 1.0.23 gifuct-js: 2.1.2 lottie-web: 5.13.0 roughjs: 4.6.6 - '@visactor/vrender@1.1.0-alpha.9': + '@visactor/vrender@1.1.0-alpha.11': dependencies: - '@visactor/vrender-animate': 1.1.0-alpha.9 - '@visactor/vrender-components': 1.1.0-alpha.9 - '@visactor/vrender-core': 1.1.0-alpha.9 - '@visactor/vrender-kits': 1.1.0-alpha.9 + '@visactor/vrender-animate': 1.1.0-alpha.11 + '@visactor/vrender-components': 1.1.0-alpha.11 + '@visactor/vrender-core': 1.1.0-alpha.11 + '@visactor/vrender-kits': 1.1.0-alpha.11 '@visactor/vscale@0.18.18': dependencies: diff --git a/docs/package.json b/docs/package.json index d8e19498e3..9d3b37d8cc 100644 --- a/docs/package.json +++ b/docs/package.json @@ -19,8 +19,8 @@ "@visactor/vchart-theme": "~1.6.6", "@visactor/vmind": "1.2.4-alpha.5", "@visactor/vutils": "~1.0.23", - "@visactor/vrender": "1.1.0-alpha.9", - "@visactor/vrender-kits": "1.1.0-alpha.9", + "@visactor/vrender": "1.1.0-alpha.11", + "@visactor/vrender-kits": "1.1.0-alpha.11", "@visactor/vtable": "1.19.0-alpha.0", "@visactor/vtable-editors": "1.19.0-alpha.0", "@visactor/vtable-gantt": "1.19.0-alpha.0", diff --git a/packages/openinula-vchart/package.json b/packages/openinula-vchart/package.json index cf107f14f2..645f23c411 100644 --- a/packages/openinula-vchart/package.json +++ b/packages/openinula-vchart/package.json @@ -30,8 +30,8 @@ "dependencies": { "@visactor/vchart": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", - "@visactor/vrender-core": "1.1.0-alpha.9", - "@visactor/vrender-kits": "1.1.0-alpha.9", + "@visactor/vrender-core": "1.1.0-alpha.11", + "@visactor/vrender-kits": "1.1.0-alpha.11", "react-is": "^18.2.0" }, "devDependencies": { diff --git a/packages/react-vchart/package.json b/packages/react-vchart/package.json index 9f362934ab..4ef51209bd 100644 --- a/packages/react-vchart/package.json +++ b/packages/react-vchart/package.json @@ -36,8 +36,8 @@ "@visactor/vchart": "workspace:2.0.22", "@visactor/vchart-extension": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", - "@visactor/vrender-core": "1.1.0-alpha.9", - "@visactor/vrender-kits": "1.1.0-alpha.9", + "@visactor/vrender-core": "1.1.0-alpha.11", + "@visactor/vrender-kits": "1.1.0-alpha.11", "react-is": "^18.2.0" }, "devDependencies": { diff --git a/packages/vchart-extension/package.json b/packages/vchart-extension/package.json index 994627a02c..b82bf84019 100644 --- a/packages/vchart-extension/package.json +++ b/packages/vchart-extension/package.json @@ -26,10 +26,10 @@ "start": "ts-node __tests__/runtime/browser/scripts/initVite.ts && vite serve __tests__/runtime/browser" }, "dependencies": { - "@visactor/vrender-core": "1.1.0-alpha.9", - "@visactor/vrender-kits": "1.1.0-alpha.9", - "@visactor/vrender-components": "1.1.0-alpha.9", - "@visactor/vrender-animate": "1.1.0-alpha.9", + "@visactor/vrender-core": "1.1.0-alpha.11", + "@visactor/vrender-kits": "1.1.0-alpha.11", + "@visactor/vrender-components": "1.1.0-alpha.11", + "@visactor/vrender-animate": "1.1.0-alpha.11", "@visactor/vchart": "workspace:2.0.22", "@visactor/vutils": "~1.0.23", "@visactor/vdataset": "~1.0.23", diff --git a/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts b/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts index 20f0745176..0c0f4f6b90 100644 --- a/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts +++ b/packages/vchart/__tests__/unit/animation/manual-ticker.test.ts @@ -185,6 +185,22 @@ const getBarClipPathRects = (chart: VChart) => path => path.baseAttributes ?? path.attribute ); +const getBarClipPathGraphics = (chart: VChart) => (getBarMarkProduct(chart).attribute.path ?? []) as AnimatedGraphic[]; + +const clickLegendItem = (chart: VChart, index: number) => { + const legendModel = chart.getComponents().find((component: any) => component.type === 'discreteLegend') as any; + const legendComponent = legendModel?._legendComponent; + const legendItem = legendComponent?._itemsContainer?.getChildren?.()[index]; + + if (!legendComponent?._onClick || !legendItem) { + throw new Error(`Expected legend item ${index} to exist`); + } + + legendComponent._onClick({ + target: legendItem + }); +}; + const createStackCornerLegendSpec = () => ({ type: 'bar', @@ -647,4 +663,62 @@ describe('manual ticker animation regressions', () => { removeDom(container); } }); + + it('keeps stack corner clip paths synced during quick legend reselect update animations', () => { + const { container, dom } = createChartContainer(); + const ticker = createManualTicker(); + const chart = new VChart(createStackCornerLegendSpec(), { + dom, + ticker, + animation: true + }); + + chart.renderSync(); + + try { + ticker.tickAt(APPEAR_DURATION + 50); + + const allSeries = Object.keys(COLOR_BY_SERIES); + const retainedSeries = allSeries.slice(0, 3); + + clickLegendItem(chart, 3); + chart.renderSync(); + + const hideUpdate = ticker.getTime(); + + ticker.tickAt(hideUpdate + UPDATE_DURATION / 10); + + clickLegendItem(chart, 3); + chart.renderSync(); + + const showUpdate = ticker.getTime(); + + ticker.tickAt(showUpdate + UPDATE_DURATION / 15); + + const clipPaths = getBarClipPathGraphics(chart); + const isAnimatingBackToAll = retainedSeries.some(seriesName => { + const graphic = getVisibleBarByFill(chart, COLOR_BY_SERIES[seriesName]); + const finalAttribute = getGraphicFinalAttribute(graphic); + + return graphic.attribute.x !== finalAttribute.x || graphic.attribute.width !== finalAttribute.width; + }); + + expect(clipPaths.length).toBe(allSeries.length); + expect(isAnimatingBackToAll).toBe(true); + + retainedSeries.forEach((seriesName, index) => { + const graphic = getVisibleBarByFill(chart, COLOR_BY_SERIES[seriesName]); + const clipPath = clipPaths[index]; + + expectClose(clipPath.attribute.x, graphic.attribute.x); + expectClose(clipPath.attribute.width, graphic.attribute.width); + expectClose(clipPath.attribute.y, graphic.attribute.y); + expectClose(clipPath.attribute.y1, graphic.attribute.y1); + }); + } finally { + chart.release(); + ticker.release(); + removeDom(container); + } + }); }); diff --git a/packages/vchart/package.json b/packages/vchart/package.json index 5667cd6aeb..796c772e43 100644 --- a/packages/vchart/package.json +++ b/packages/vchart/package.json @@ -126,11 +126,11 @@ "@visactor/vdataset": "~1.0.23", "@visactor/vscale": "~1.0.23", "@visactor/vlayouts": "~1.0.23", - "@visactor/vrender": "1.1.0-alpha.9", - "@visactor/vrender-core": "1.1.0-alpha.9", - "@visactor/vrender-kits": "1.1.0-alpha.9", - "@visactor/vrender-components": "1.1.0-alpha.9", - "@visactor/vrender-animate": "1.1.0-alpha.9", + "@visactor/vrender": "1.1.0-alpha.11", + "@visactor/vrender-core": "1.1.0-alpha.11", + "@visactor/vrender-kits": "1.1.0-alpha.11", + "@visactor/vrender-components": "1.1.0-alpha.11", + "@visactor/vrender-animate": "1.1.0-alpha.11", "@visactor/vutils-extension": "workspace:2.0.22" }, "publishConfig": { diff --git a/tools/story-player/package.json b/tools/story-player/package.json index 69247dd3b7..14e56dc3f4 100644 --- a/tools/story-player/package.json +++ b/tools/story-player/package.json @@ -56,10 +56,10 @@ "vite": "3.2.6" }, "dependencies": { - "@visactor/vrender-core": "1.1.0-alpha.9", - "@visactor/vrender-kits": "1.1.0-alpha.9", + "@visactor/vrender-core": "1.1.0-alpha.11", + "@visactor/vrender-kits": "1.1.0-alpha.11", "@visactor/vchart": "workspace:2.0.22", - "@visactor/vrender": "1.1.0-alpha.9", + "@visactor/vrender": "1.1.0-alpha.11", "@visactor/vutils": "~1.0.23" } }