Skip to content

Commit 35d566f

Browse files
authored
Merge pull request #4039 from VisActor/fix/aniamtion-state
Fix/aniamtion state
2 parents c9ea810 + 8f4fb0e commit 35d566f

File tree

11 files changed

+248
-207
lines changed

11 files changed

+248
-207
lines changed

packages/vchart-schema/vchart.json

Lines changed: 118 additions & 120 deletions
Large diffs are not rendered by default.

packages/vchart/src/component/player/player.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ export class Player extends BaseComponent<IPlayer> implements IComponent {
330330
// 自动播放
331331
this._option.globalInstance.on(ChartEvent.rendered, () => {
332332
if (this._spec?.auto) {
333+
this._playerComponent.pause();
333334
this._playerComponent.play();
334335
}
335336
});

packages/vchart/src/core/vchart.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -793,7 +793,6 @@ export class VChart implements IVChart {
793793
if (!this._beforeRender(option)) {
794794
return self;
795795
}
796-
this._updateAnimateState(true);
797796
// 填充数据绘图
798797
this._compiler?.render(option.morphConfig);
799798
this._updateAnimateState(false);
@@ -811,7 +810,7 @@ export class VChart implements IVChart {
811810
const updateGraphicAnimationState = (graphic: IMarkGraphic) => {
812811
const diffState = graphic.context?.diffState;
813812
if (initial) {
814-
return diffState === 'exit' ? undefined : AnimationStateEnum.appear;
813+
return diffState === 'exit' ? AnimationStateEnum.none : AnimationStateEnum.appear;
815814
}
816815
return diffState;
817816
};
@@ -939,13 +938,12 @@ export class VChart implements IVChart {
939938
if (this._chart) {
940939
this._chart.updateData(id, data, true, parserOptions);
941940

942-
// after layout
943-
this._compiler.render();
944-
945941
if (userUpdateOptions?.reAnimate) {
946942
this.stopAnimation();
947943
this._updateAnimateState(true);
948944
}
945+
this._compiler.render();
946+
949947
return this as unknown as IVChart;
950948
}
951949
this._spec.data = array(this._spec.data);
@@ -968,11 +966,12 @@ export class VChart implements IVChart {
968966
if (this._chart) {
969967
this._chart.updateFullData(data);
970968
if (reRender) {
971-
this._compiler.render();
972969
if (userUpdateOptions?.reAnimate) {
973970
this.stopAnimation();
974971
this._updateAnimateState(true);
975972
}
973+
974+
this._compiler.render();
976975
}
977976
return this as unknown as IVChart;
978977
}

packages/vchart/src/mark/base/base-mark.ts

Lines changed: 71 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ import type {
5151
IMarkStateManager,
5252
StateValueType
5353
} from '../../compile/mark/interface';
54-
import { array, degreeToRadian, has, isArray, isBoolean, isFunction, isNil, isObject, isValid } from '@visactor/vutils';
54+
import { array, degreeToRadian, isArray, isBoolean, isFunction, isNil, isObject, isValid } from '@visactor/vutils';
5555
import { curveTypeTransform, groupData, runEncoder } from '../utils/common';
5656
import type { ICompilableInitOption } from '../../compile/interface';
5757
import { LayoutState } from '../../compile/interface';
@@ -67,7 +67,8 @@ import type { ICompilableData } from '../../compile/data/interface';
6767
import type { IAnimationConfig } from '../../animation/interface';
6868
import { AnimationStateEnum, type MarkAnimationSpec } from '../../animation/interface';
6969
import { CompilableData } from '../../compile/data/compilable-data';
70-
import { log } from '../../util';
70+
import { getDiffAttributesOfGraphic } from '../../util/mark';
71+
import { log } from '../../util/debug';
7172
import { morph as runMorph } from '../../compile/morph';
7273

7374
export type ExChannelCall = (
@@ -1074,6 +1075,7 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
10741075
this._dataByKey = (mark as any)._dataByKey;
10751076
this._prevDataByKey = (mark as any)._prevDataByKey;
10761077
this.needClear = (mark as any).needClear;
1078+
this._aniamtionStateCallback = (mark as any)._aniamtionStateCallback;
10771079
}
10781080

10791081
private _parseProgressiveContext(data: Datum[]) {
@@ -1145,12 +1147,18 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
11451147
// TODO 因为数据的覆盖特点,无动画的时候新的更新一定会覆盖前一次的旧值,所以默认都是后面的动画覆盖前面的动画
11461148
// TODO 但是如果用户定义了一个动画数组,他的预期是动画不会覆盖,通过priority为INfinity来控制不覆盖
11471149
if (Array.isArray(config)) {
1148-
config = config.map((item: any, index: number) => ({
1150+
return config.map((item: any, index: number) => ({
11491151
...item,
11501152
priority: item.priority ?? Infinity
11511153
}));
11521154
}
1153-
return config;
1155+
return config
1156+
? {
1157+
...config,
1158+
// 循环动画的优先级定为最高,不会被屏蔽掉
1159+
priority: type === 'normal' ? config.priority ?? Infinity : config.priority
1160+
}
1161+
: config;
11541162
}
11551163

11561164
/**
@@ -1177,6 +1185,7 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
11771185
if (!this._animationConfig || graphics.length === 0) {
11781186
return;
11791187
}
1188+
11801189
if (this.tryRunMorphing(graphics)) {
11811190
return;
11821191
}
@@ -1262,10 +1271,29 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
12621271
}
12631272
}
12641273

1274+
protected _setAnimationState(g: IMarkGraphic) {
1275+
const customizedState = this._aniamtionStateCallback ? this._aniamtionStateCallback(g) : undefined;
1276+
1277+
g.context.animationState = customizedState ?? g.context.diffState;
1278+
1279+
// 复用exit的图元,需要设置属性为最初的属性
1280+
if (g.context.animationState === DiffState.exit) {
1281+
// 表示正在被复用,后续需要重设属性的
1282+
g.context.reusing = true;
1283+
// 停止所有动画,
1284+
// TODO:属性可能回不去了(如果enter和exit不是一个动画),所以在encode阶段要获取finalAttribute,设置上去
1285+
(g as any).animates && (g as any).animates.forEach((a: any) => a.stop());
1286+
// force element to stop exit animation if it is reentered
1287+
// todo animaiton
1288+
// const animators = this.animate?.getElementAnimators(element, DiffState.exit);
1289+
// animators && animators.forEach(animator => animator.stop('start'));
1290+
}
1291+
}
1292+
12651293
protected _runJoin(data: Datum[]) {
12661294
const newGroupedData = this._getDataByKey(data);
12671295
const prevGroupedData = this._prevDataByKey;
1268-
const newGraphics: IMarkGraphic[] = [];
1296+
const allGraphics: IMarkGraphic[] = [];
12691297

12701298
const enterGraphics = new Set<IMarkGraphic>(this._graphics.filter(g => g.context.diffState === DiffState.enter));
12711299

@@ -1289,32 +1317,20 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
12891317
diffState = DiffState.enter;
12901318
g.isExiting = false;
12911319

1292-
// 复用exit的图元,需要设置属性为最初的属性
1293-
if (g.context?.diffState === DiffState.exit) {
1294-
// 表示正在被复用,后续需要重设属性的
1295-
g.context.reusing = true;
1296-
// 停止所有动画,
1297-
// TODO:属性可能回不去了(如果enter和exit不是一个动画),所以在encode阶段要获取finalAttribute,设置上去
1298-
(g as any).animates && (g as any).animates.forEach((a: any) => a.stop());
1299-
// force element to stop exit animation if it is reentered
1300-
// todo animaiton
1301-
// const animators = this.animate?.getElementAnimators(element, DiffState.exit);
1302-
// animators && animators.forEach(animator => animator.stop('start'));
1303-
}
1304-
13051320
this._graphicMap.set(key, g as IMarkGraphic);
1306-
newGraphics.push(g as IMarkGraphic);
1321+
allGraphics.push(g as IMarkGraphic);
13071322
} else {
13081323
// update
13091324
g = this._graphicMap.get(key);
13101325

13111326
if (g) {
13121327
diffState = DiffState.update;
1313-
newGraphics.push(g as IMarkGraphic);
1328+
allGraphics.push(g);
13141329
}
13151330
}
13161331

13171332
if (g) {
1333+
enterGraphics.delete(g);
13181334
g.context = {
13191335
...this._getCommonContext(),
13201336
diffState,
@@ -1328,7 +1344,6 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
13281344
fieldX: g.context?.fieldX,
13291345
// 从旧context中继承
13301346
fieldY: g.context?.fieldY,
1331-
animationState: diffState,
13321347
// TODO 如果newData为空,则使用旧的data,避免exit图元找不到data
13331348
data: newData ?? g.context?.data,
13341349
uniqueKey: key,
@@ -1338,7 +1353,7 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
13381353
indexKey: '__VCHART_DEFAULT_DATA_INDEX',
13391354
stateAnimateConfig: this.getAnimationConfig()?.state
13401355
};
1341-
enterGraphics.delete(g);
1356+
this._setAnimationState(g);
13421357
}
13431358
return g;
13441359
};
@@ -1363,13 +1378,15 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
13631378
const g = callback(key, newGroupedData.data.get(key), null);
13641379
if (g) {
13651380
g.context.animationState = AnimationStateEnum.appear;
1381+
// this._setAnimationState(g);
13661382
}
13671383
});
13681384
} else if (prevGroupedData) {
13691385
prevGroupedData.keys.forEach(key => {
13701386
// disappear
13711387
const g = callback(key, null, prevGroupedData.data.get(key));
13721388
g.context.animationState = AnimationStateEnum.disappear;
1389+
// this._setAnimationState(g);
13731390
});
13741391
}
13751392

@@ -1383,13 +1400,13 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
13831400

13841401
(g as IMarkGraphic).release();
13851402
});
1386-
const graphicCount = newGraphics.length;
1387-
newGraphics.forEach((g, index) => {
1403+
const graphicCount = allGraphics.length;
1404+
allGraphics.forEach((g, index) => {
13881405
g.context.graphicCount = graphicCount;
13891406
g.context.graphicIndex = index;
13901407
});
13911408
this._dataByKey = newGroupedData;
1392-
this._graphics = newGraphics;
1409+
this._graphics = allGraphics;
13931410
this.needClear = true;
13941411
}
13951412

@@ -1543,14 +1560,7 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
15431560
this._graphicMap.set(g.context.uniqueKey, g);
15441561
}
15451562
} else {
1546-
// diff一下,获取差异的属性
1547-
const prevAttrs: Record<string, any> = g.getAttributes(true);
1548-
const diffAttrs: Record<string, any> = {};
1549-
Object.keys(finalAttrs).forEach(key => {
1550-
if (prevAttrs[key] !== finalAttrs[key]) {
1551-
diffAttrs[key] = finalAttrs[key];
1552-
}
1553-
});
1563+
const diffAttrs = getDiffAttributesOfGraphic(g, finalAttrs);
15541564
g.context.diffAttrs = diffAttrs;
15551565
if (g.context.reusing) {
15561566
// 表示正在被复用,需要重设属性的
@@ -1759,29 +1769,30 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
17591769
}
17601770
};
17611771
this._graphicMap.forEach((g, key) => {
1772+
if (g.context.diffState !== DiffState.exit || g.isExiting) {
1773+
return;
1774+
}
17621775
// 避免重复执行退场动画
1763-
if (g.context.diffState === DiffState.exit && !g.isExiting) {
1764-
if (this.hasAnimationByState('exit')) {
1765-
g.isExiting = true;
1766-
// 执行exit动画
1767-
const animationConfig = this.getAnimationConfig();
1768-
if ((animationConfig as any).exit && (animationConfig as any).exit.length) {
1769-
const exitConfigList = (animationConfig as any).exit.map((item: any, index: number) => ({
1770-
name: `exit_${index}`,
1771-
animation: {
1772-
...item,
1773-
customParameters: g.context
1774-
}
1775-
}));
1776-
g.applyAnimationState(['exit'], [exitConfigList.length === 1 ? exitConfigList[0] : exitConfigList], () => {
1777-
// 有可能又被复用了,所以这里需要判断,如果还是在exiting阶段的话才删除
1778-
// TODO 这里如果频繁执行的话,可能会误判
1779-
doRemove(g, key);
1780-
});
1781-
}
1782-
} else {
1783-
doRemove(g, key);
1776+
if (g.context.animationState === DiffState.exit && this.hasAnimationByState('exit')) {
1777+
g.isExiting = true;
1778+
// 执行exit动画
1779+
const animationConfig = this.getAnimationConfig();
1780+
if ((animationConfig as any).exit && (animationConfig as any).exit.length) {
1781+
const exitConfigList = (animationConfig as any).exit.map((item: any, index: number) => ({
1782+
name: `exit_${index}`,
1783+
animation: {
1784+
...item,
1785+
customParameters: g.context
1786+
}
1787+
}));
1788+
g.applyAnimationState(['exit'], [exitConfigList.length === 1 ? exitConfigList[0] : exitConfigList], () => {
1789+
// 有可能又被复用了,所以这里需要判断,如果还是在exiting阶段的话才删除
1790+
// TODO 这里如果频繁执行的话,可能会误判
1791+
doRemove(g, key);
1792+
});
17841793
}
1794+
} else {
1795+
doRemove(g, key);
17851796
}
17861797
});
17871798
}
@@ -1966,19 +1977,17 @@ export class BaseMark<T extends ICommonSpec> extends GrammarItem implements IMar
19661977
}
19671978
}
19681979

1980+
protected _aniamtionStateCallback: (g: IMarkGraphic) => AnimationStateValues;
1981+
19691982
updateAnimationState(callback: (graphic: IMarkGraphic) => AnimationStateValues) {
1970-
if (this._graphics && this._graphics.length) {
1971-
this._graphics.forEach(g => {
1972-
g.context.animationState = callback(g);
1973-
});
1974-
}
1983+
this._aniamtionStateCallback = callback;
19751984
}
19761985

1977-
hasAnimationByState(state: keyof MarkAnimationSpec) {
1978-
if (!state || !this._animationConfig || !this._animationConfig[state]) {
1986+
hasAnimationByState(state: AnimationStateValues) {
1987+
if (!state || !this._animationConfig || !(this._animationConfig as any)[state]) {
19791988
return false;
19801989
}
1981-
const stateAnimationConfig = this._animationConfig[state];
1990+
const stateAnimationConfig = (this._animationConfig as any)[state];
19821991
return (stateAnimationConfig as IAnimationConfig[]).length > 0 || isObject(stateAnimationConfig);
19831992
}
19841993

packages/vchart/src/mark/group.ts

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,22 @@ import type { Maybe } from '../typings';
44
import { log, warn } from '../util/debug';
55
import type { IGroupMarkSpec } from '../typings/visual';
66
import { BaseMark } from './base/base-mark';
7-
import type { AnimationStateValues, IGroupMark, IMark, IMarkGraphic, MarkType } from './interface';
7+
import {
8+
DiffState,
9+
type AnimationStateValues,
10+
type IGroupMark,
11+
type IMark,
12+
type IMarkGraphic,
13+
type MarkType
14+
} from './interface';
815
// eslint-disable-next-line no-duplicate-imports
916
import { MarkTypeEnum } from './interface/type';
1017
import { type IMarkCompileOption } from '../compile/mark';
1118
import type { IGroup, IGroupGraphicAttribute } from '@visactor/vrender-core';
1219
import { registerGroup, registerShadowRoot } from '@visactor/vrender-kits';
1320
import { isNil } from '@visactor/vutils';
1421
import { traverseGroupMark } from '../compile/util';
15-
import { LayoutState } from '../compile/interface';
22+
import { getDiffAttributesOfGraphic } from '../util/mark';
1623

1724
export class GroupMark extends BaseMark<IGroupMarkSpec> implements IGroupMark {
1825
static readonly type = MarkTypeEnum.group;
@@ -110,9 +117,33 @@ export class GroupMark extends BaseMark<IGroupMarkSpec> implements IGroupMark {
110117
}
111118

112119
const style = this._simpleStyle ?? this.getAttributesOfState({});
120+
const prevState = this._product.context?.diffState;
121+
122+
this._product.context = {
123+
...this._product.context,
124+
...this._getCommonContext(),
125+
diffState: prevState ? DiffState.update : DiffState.enter
126+
};
127+
this._setAnimationState(this._product as unknown as IMarkGraphic);
128+
const newAttrs = this._getAttrsFromConfig(style);
129+
130+
// TODO: 需要优化,现在group mark 走了一些特殊逻辑
131+
if (this._product.context.diffState === DiffState.update) {
132+
const hasAnimation = this.hasAnimation();
133+
const diffAttrs = getDiffAttributesOfGraphic(this._product as unknown as IMarkGraphic, newAttrs);
134+
this._product.context.diffAttrs = diffAttrs;
135+
136+
if (!this.hasAnimationByState(this._product.context.animationState)) {
137+
hasAnimation ? this._product.setAttributesAndPreventAnimate(diffAttrs) : this._product.setAttributes(diffAttrs);
138+
}
139+
140+
if (hasAnimation) {
141+
this._product.setFinalAttributes(newAttrs);
142+
}
143+
} else {
144+
this._product.setAttributes(newAttrs);
145+
}
113146

114-
this._product.context = { ...this._product.context, ...this._getCommonContext() };
115-
this._product.setAttributes(this._getAttrsFromConfig(style));
116147
this.needClear = true;
117148
}
118149

@@ -129,11 +160,8 @@ export class GroupMark extends BaseMark<IGroupMarkSpec> implements IGroupMark {
129160
}
130161

131162
updateAnimationState(callback: (g: IMarkGraphic) => AnimationStateValues) {
132-
this.getGraphics().forEach(g => {
133-
if (g) {
134-
g.context = { ...g.context, animationState: callback(g) };
135-
}
136-
});
163+
super.updateAnimationState(callback);
164+
137165
this.getMarks().forEach(mark => {
138166
mark.updateAnimationState(callback);
139167
});

0 commit comments

Comments
 (0)