Skip to content

Commit c6b941a

Browse files
authored
fix(animation): 动画添加回调, 可以给不同的 形状添加动画 (#3275)
Co-authored-by: ai-qing-hai <[email protected]>
1 parent fecd1e3 commit c6b941a

File tree

12 files changed

+286
-88
lines changed

12 files changed

+286
-88
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { Facet } from '../../../../src';
2+
import { createDiv } from '../../../utils/dom';
3+
4+
describe('facet animation', () => {
5+
const data = [
6+
{ type: '1', date: '2014-01', value: 1, name: 'a' },
7+
{ type: '1', date: '2015-01', value: 1, name: 'b' },
8+
{ type: '1', date: '2016-01', value: 1, name: 'c' },
9+
{ type: '1', date: '2017-01', value: 1, name: 'd' },
10+
{ type: '2', date: '2014-01', value: 1, name: 'a' },
11+
{ type: '2', date: '2015-01', value: 1, name: 'b' },
12+
{ type: '2', date: '2016-01', value: 1, name: 'a' },
13+
{ type: '2', date: '2017-01', value: 1, name: 'd' },
14+
{ type: '3', date: '2014-01', value: 1, name: 'b' },
15+
{ type: '4', date: '2015-01', value: 1, name: 'd' },
16+
{ type: '4', date: '2016-01', value: 10, name: 'b' },
17+
{ type: '4', date: '2017-01', value: 1, name: 'c' },
18+
];
19+
const plot = new Facet(createDiv(), {
20+
data,
21+
type: 'rect',
22+
fields: ['type'],
23+
eachView: () => {
24+
return {
25+
geometries: [
26+
{ type: 'interval', xField: 'date', yField: 'value', colorField: 'name', mapping: {} },
27+
{ type: 'point', xField: 'date', yField: 'value', colorField: 'name', mapping: {} },
28+
],
29+
animation: {
30+
appear: {
31+
animation: 'fade-in',
32+
duration: 3500,
33+
},
34+
leave: {
35+
animation: 'wave-in',
36+
duration: 200,
37+
},
38+
},
39+
};
40+
},
41+
meta: { date: { sync: true } },
42+
});
43+
plot.render();
44+
45+
it('default animation', () => {
46+
const geometries = plot.chart.views[0].geometries;
47+
48+
expect(geometries[0].animateOption).toMatchObject({
49+
appear: {
50+
animation: 'fade-in',
51+
duration: 3500,
52+
},
53+
leave: {
54+
animation: 'wave-in',
55+
duration: 200,
56+
},
57+
});
58+
});
59+
60+
it('callback animation', () => {
61+
plot.update({
62+
eachView: () => {
63+
return {
64+
geometries: [
65+
{ type: 'interval', xField: 'date', yField: 'value', colorField: 'name', mapping: {} },
66+
{ type: 'point', xField: 'date', yField: 'value', colorField: 'name', mapping: {} },
67+
],
68+
animation: (type) => ({
69+
appear: {
70+
animation: type === 'point' ? 'fade-in' : 'wave-in',
71+
duration: 500,
72+
},
73+
leave: {
74+
animation: type === 'interval' ? 'fade-out' : 'wave-out',
75+
duration: 300,
76+
},
77+
}),
78+
};
79+
},
80+
});
81+
82+
const geometries = plot.chart.views[0].geometries;
83+
84+
expect(geometries[0].animateOption).toMatchObject({
85+
appear: {
86+
animation: 'wave-in',
87+
duration: 500,
88+
},
89+
leave: {
90+
animation: 'fade-out',
91+
duration: 300,
92+
},
93+
});
94+
95+
expect(geometries[1].animateOption).toMatchObject({
96+
appear: {
97+
animation: 'fade-in',
98+
duration: 500,
99+
},
100+
leave: {
101+
animation: 'wave-out',
102+
duration: 300,
103+
},
104+
});
105+
});
106+
107+
afterAll(() => {
108+
plot.destroy();
109+
});
110+
});

__tests__/unit/plots/line/animation-spec.ts

+59-25
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,40 @@ import { partySupport } from '../../../data/party-support';
33
import { createDiv } from '../../../utils/dom';
44

55
describe('line', () => {
6-
it('x*y with animation', () => {
7-
const line = new Line(createDiv(), {
8-
width: 400,
9-
height: 300,
10-
data: partySupport.filter((o) => ['FF'].includes(o.type)),
11-
xField: 'date',
12-
yField: 'value',
13-
appendPadding: 10,
14-
smooth: true,
15-
animation: {
16-
enter: {
17-
animation: 'fade-in',
18-
},
19-
leave: {
20-
animation: 'fade-out',
21-
},
6+
const line = new Line(createDiv(), {
7+
width: 400,
8+
height: 300,
9+
data: partySupport.filter((o) => ['FF'].includes(o.type)),
10+
xField: 'date',
11+
yField: 'value',
12+
appendPadding: 10,
13+
smooth: true,
14+
animation: {
15+
enter: {
16+
animation: 'fade-in',
2217
},
23-
});
18+
leave: {
19+
animation: 'fade-out',
20+
},
21+
},
22+
point: {},
23+
});
2424

25-
line.render();
25+
line.render();
2626

27+
it('x*y with animation', () => {
2728
// 追加默认的动画配置
2829
expect(line.chart.geometries[0].animateOption).toEqual({
30+
enter: {
31+
duration: 400,
32+
easing: 'easeQuadInOut',
33+
animation: 'fade-in',
34+
},
35+
leave: {
36+
duration: 350,
37+
easing: 'easeQuadIn',
38+
animation: 'fade-out',
39+
},
2940
appear: {
3041
duration: 450,
3142
easing: 'easeQuadOut',
@@ -34,15 +45,38 @@ describe('line', () => {
3445
duration: 400,
3546
easing: 'easeQuadInOut',
3647
},
37-
enter: {
38-
duration: 400,
39-
easing: 'easeQuadInOut',
40-
animation: 'fade-in',
48+
});
49+
});
50+
51+
it('x*y with animation callback', () => {
52+
line.update({
53+
animation: (type) => ({
54+
appear: {
55+
animation: type === 'line' ? 'wave-in' : 'fade-in',
56+
duration: type === 'line' ? 4500 : 1000,
57+
delay: type === 'line' ? 0 : 4500,
58+
easing: 'easeQuadIn',
59+
},
60+
}),
61+
});
62+
63+
// 追加默认的动画配置
64+
expect(line.chart.geometries[0].animateOption).toMatchObject({
65+
appear: {
66+
animation: 'wave-in',
67+
duration: 4500,
68+
delay: 0,
69+
easing: 'easeQuadIn',
4170
},
42-
leave: {
43-
duration: 350,
71+
});
72+
73+
// 追加默认的动画配置
74+
expect(line.chart.geometries[1].animateOption).toMatchObject({
75+
appear: {
76+
animation: 'fade-in',
77+
duration: 1000,
78+
delay: 4500,
4479
easing: 'easeQuadIn',
45-
animation: 'fade-out',
4680
},
4781
});
4882

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { Sankey } from '../../../../src';
2+
import { createDiv } from '../../../utils/dom';
3+
import { ENERGY_RELATIONS } from '../../../data/sankey-energy';
4+
5+
describe('sankey animation', () => {
6+
const sankey = new Sankey(createDiv(), {
7+
height: 500,
8+
data: ENERGY_RELATIONS,
9+
sourceField: 'source',
10+
targetField: 'target',
11+
weightField: 'value',
12+
animation: {
13+
appear: {
14+
animation: 'fade-in',
15+
duration: 300,
16+
},
17+
leave: {
18+
animation: 'fade-out',
19+
duration: 350,
20+
},
21+
},
22+
});
23+
24+
sankey.render();
25+
26+
it('sankey animation', () => {
27+
expect(sankey.options.animation).toMatchObject({
28+
appear: {
29+
animation: 'fade-in',
30+
duration: 300,
31+
},
32+
leave: {
33+
animation: 'fade-out',
34+
duration: 350,
35+
},
36+
});
37+
});
38+
39+
it('sankey animation callback', () => {
40+
sankey.update({
41+
animation: (type) => ({
42+
appear: {
43+
animation: type === 'edge' ? 'wave-in' : 'fade-in',
44+
duration: type === 'polygon' ? 4000 : 2000,
45+
},
46+
}),
47+
});
48+
49+
const geometries = [...sankey.chart.views[0].geometries, ...sankey.chart.views[1].geometries];
50+
51+
expect(geometries[0].animateOption).toMatchObject({
52+
appear: {
53+
animation: 'wave-in',
54+
duration: 2000,
55+
},
56+
});
57+
58+
expect(geometries[1].animateOption).toMatchObject({
59+
appear: {
60+
animation: 'fade-in',
61+
duration: 4000,
62+
},
63+
});
64+
65+
sankey.destroy();
66+
});
67+
});

src/adaptor/common.ts

+2-11
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Interaction } from '../types/interaction';
66
import { Transformations } from '../types/coordinate';
77
import { Axis } from '../types/axis';
88
import { AXIS_META_CONFIG_KEYS } from '../constant';
9-
import { pick, deepAssign } from '../utils';
9+
import { pick, deepAssign, addViewAnimation } from '../utils';
1010

1111
/**
1212
* 通用 legend 配置, 适用于带 colorField 或 seriesField 的图表
@@ -69,17 +69,8 @@ export function animation<O extends Pick<Options, 'animation'>>(params: Params<O
6969
const { chart, options } = params;
7070
const { animation } = options;
7171

72-
// 同时设置整个 view 动画选项
73-
if (typeof animation === 'boolean') {
74-
chart.animate(animation);
75-
} else {
76-
chart.animate(true);
77-
}
78-
7972
// 所有的 Geometry 都使用同一动画(各个图形如有区别,自行覆盖)
80-
each(chart.geometries, (g: Geometry) => {
81-
g.animate(animation);
82-
});
73+
addViewAnimation(chart, animation);
8374

8475
return params;
8576
}

src/plots/chord/adaptor.ts

+2-14
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import { Geometry } from '@antv/g2';
2-
import { each } from '@antv/util';
31
import { interaction, theme, state } from '../../adaptor/common';
42
import { Params } from '../../core/adaptor';
53
import { flow, pick } from '../../utils';
64
import { polygon, edge } from '../../adaptor/geometries';
75
import { chordLayout } from '../../utils/transform/chord';
8-
import { getAllGeometriesRecursively, transformDataToNodeLinkData } from '../../utils';
6+
import { getAllGeometriesRecursively, transformDataToNodeLinkData, addViewAnimation } from '../../utils';
97
import { ChordOptions } from './types';
108
import { X_FIELD, Y_FIELD, NODE_COLOR_FIELD, EDGE_COLOR_FIELD } from './constant';
119

@@ -170,17 +168,7 @@ function animation(params: Params<ChordOptions>): Params<ChordOptions> {
170168
const { chart, options } = params;
171169
const { animation } = options;
172170

173-
// 同时设置整个 view 动画选项
174-
if (typeof animation === 'boolean') {
175-
chart.animate(animation);
176-
} else {
177-
chart.animate(true);
178-
}
179-
180-
// 所有的 Geometry 都使用同一动画(各个图形如有区别,自行覆盖)
181-
each(getAllGeometriesRecursively(chart), (g: Geometry) => {
182-
g.animate(animation);
183-
});
171+
addViewAnimation(chart, animation, getAllGeometriesRecursively(chart));
184172

185173
return params;
186174
}

src/plots/facet/types.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export type IView = {
5454
/**
5555
* @title animation 配置
5656
*/
57-
readonly animation?: Animation;
57+
readonly animation?: Options['animation'];
5858
/**
5959
* @title tooltip 配置
6060
*/

src/plots/facet/utils.ts

+4-12
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Geometry, View } from '@antv/g2';
1+
import { View } from '@antv/g2';
22
import { each } from '@antv/util';
33
import { geometry as geometryAdaptor } from '../../adaptor/geometries/base';
44
import { AXIS_META_CONFIG_KEYS } from '../../constant';
5-
import { pick, deepAssign } from '../../utils';
6-
import { Axis, Interaction } from '../../types';
5+
import { pick, deepAssign, addViewAnimation } from '../../utils';
6+
import { Axis, Interaction, Options } from '../../types';
77
import { IView } from './types';
88

99
/**
@@ -74,15 +74,7 @@ export function execViewAdaptor(viewOfG2: View, options: IView): void {
7474
});
7575

7676
// 7. animation (先做动画)
77-
if (typeof animation === 'boolean') {
78-
viewOfG2.animate(false);
79-
} else {
80-
viewOfG2.animate(true);
81-
// 所有的 Geometry 都使用同一动画(各个图形如有区别,todo 自行覆盖)
82-
each(viewOfG2.geometries, (g: Geometry) => {
83-
g.animate(animation);
84-
});
85-
}
77+
addViewAnimation(viewOfG2, animation as Options['animation']);
8678

8779
if (tooltip) {
8880
// 8. tooltip

0 commit comments

Comments
 (0)