Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(region-filter): add geometry life circle, after draw animate #2879

Merged
merged 4 commits into from
Sep 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions src/chart/controller/annotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
} from '../../interface';

import { DEFAULT_ANIMATE_CFG } from '../../animate/';
import { COMPONENT_TYPE, DIRECTION, LAYER, VIEW_LIFE_CIRCLE } from '../../constant';
import { COMPONENT_TYPE, DIRECTION, GEOMETRY_LIFE_CIRCLE, LAYER, VIEW_LIFE_CIRCLE } from '../../constant';

import Geometry from '../../geometry/base';
import Element from '../../geometry/element';
Expand Down Expand Up @@ -63,6 +63,7 @@ export default class Annotation extends Controller<BaseOption[]> {
const theme = this.getAnnotationTheme(type);

component.update(this.getAnnotationCfg(type, extra, theme));
component.render();
};
const createComponentFn = (option: BaseOption) => {
const co = this.createAnnotation(option);
Expand All @@ -84,7 +85,7 @@ export default class Annotation extends Controller<BaseOption[]> {

if (component.get('type') === 'regionFilter') {
// regionFilter 依赖绘制后的 Geometry Shapes
this.view.getRootView().once(VIEW_LIFE_CIRCLE.AFTER_RENDER, () => {
this.whenRegionFilter(() => {
updateComponentFn(co);
});
} else {
Expand All @@ -94,8 +95,7 @@ export default class Annotation extends Controller<BaseOption[]> {
} else {
each(this.option, (option: BaseOption) => {
if (option.type === 'regionFilter') {
this.view.getRootView().once(VIEW_LIFE_CIRCLE.AFTER_RENDER, () => {
// regionFilter 依赖绘制后的 Geometry Shapes
this.whenRegionFilter(() => {
createComponentFn(option);
});
} else {
Expand Down Expand Up @@ -147,7 +147,7 @@ export default class Annotation extends Controller<BaseOption[]> {
}
};

this.view.once(VIEW_LIFE_CIRCLE.AFTER_RENDER, () => {
this.whenRegionFilter(() => {
// 先看是否有 regionFilter 要更新
each(this.option, (option: BaseOption) => {
if (option.type === 'regionFilter') {
Expand Down Expand Up @@ -215,6 +215,27 @@ export default class Annotation extends Controller<BaseOption[]> {
return co;
}

/**
* region filter 比较特殊的渲染时机
* @param doWhat
*/
private whenRegionFilter(doWhat: () => void) {
if (this.view.getOptions().animate) {
this.view.geometries.forEach((g: Geometry) => {
// 如果 geometry 开启,则监听
if (g.animateOption) {
g.once(GEOMETRY_LIFE_CIRCLE.AFTER_DRAW_ANIMATE, () => {
doWhat();
});
}
})
} else {
this.view.getRootView().once(VIEW_LIFE_CIRCLE.AFTER_RENDER, () => {
doWhat();
});
}
}

private createAnnotation(option: BaseOption) {
const { type } = option;

Expand Down
8 changes: 8 additions & 0 deletions src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ export enum VIEW_LIFE_CIRCLE {
AFTER_CHANGE_SIZE = 'afterchangesize',
}

/**
* geometry 的生命周期
*/
export enum GEOMETRY_LIFE_CIRCLE {
BEFORE_DRAW_ANIMATE = 'beforeanimate',
AFTER_DRAW_ANIMATE = 'afteranimate',
}

/**
* 绘图区的事件列表
*/
Expand Down
22 changes: 19 additions & 3 deletions src/geometry/element/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { deepMix, each, get, isArray, isFunction, isString } from '@antv/util';
import { propagationDelegate } from '@antv/component/lib/util/event';
import { doAnimate } from '../../animate';
import Base from '../../base';
import { BBox, IGroup, IShape } from '../../dependents';
import { AnimateOption, Datum, ShapeFactory, ShapeInfo, StateCfg } from '../../interface';
import { getReplaceAttrs } from '../../util/graphics';
import Geometry from '../base';

import { propagationDelegate } from '@antv/component/lib/util/event';
import { GEOMETRY_LIFE_CIRCLE } from '../../constant';

/** Element 构造函数传入参数类型 */
interface ElementCfg {
Expand Down Expand Up @@ -361,7 +361,19 @@ export default class Element extends Base {
private getAnimateCfg(animateType: string) {
const animate = this.animate;
if (animate) {
return animate[animateType];
const cfg = animate[animateType];

if (cfg) {
// 增加动画的回调函数,如果外部传入了,则先执行外部,然后发射 geometry 的 animate 事件
return {
...cfg,
callback: () => {
isFunction(cfg.callback) && cfg.callback();
this.geometry?.emit(GEOMETRY_LIFE_CIRCLE.AFTER_DRAW_ANIMATE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个是 element 没执行完一次动画就发射了啊。

这个是不是应该是在 view 层发射动画结束的事件更合适?我记得 G 里面是有几个标识位的:

image

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我想过了,不冲突。

view 里面做动画是否结束,需要看他的 geometry 还是子 view 的 geometry,太难了啊,还没有想清楚,如果要做 view 上的,肯定是组合他下面的 geometry 的动画结束事件,然后发送一个 view 的动画结束事件。

所以现在 geometry 上做掉,解决一部分问题。

},
}
}
return cfg;
}

return null;
Expand Down Expand Up @@ -391,6 +403,9 @@ export default class Element extends Base {
const animateType = isUpdate ? 'enter' : 'appear';
const animateCfg = this.getAnimateCfg(animateType);
if (animateCfg) {
// 开始执行动画的生命周期
this.geometry?.emit(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE);

doAnimate(this.shape, animateCfg, {
coordinate: shapeFactory.coordinate,
toAttrs: {
Expand Down Expand Up @@ -461,6 +476,7 @@ export default class Element extends Base {

if (this.animate) {
if (animateCfg) {
this.geometry?.emit(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE);
// 需要进行动画
doAnimate(sourceShape, animateCfg, {
coordinate: this.shapeFactory.coordinate,
Expand Down
61 changes: 61 additions & 0 deletions tests/bugs/2851-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Chart } from '../../src';
import { createDiv } from '../util/dom';
import { delay } from '../util/delay';

describe('2851', () => {
it('2851', async () => {
const data = [
{ year: '1951 年', sales: 280 },
{ year: '1952 年', sales: 52 },
{ year: '1956 年', sales: 61 },
{ year: '1957 年', sales: 145 },
{ year: '1958 年', sales: 48 },
{ year: '1959 年', sales: 38 },
{ year: '1960 年', sales: 38 },
{ year: '1962 年', sales: 38 },
];

const chart = new Chart({
container: createDiv(),
width: 400,
height: 300,
autoFit: true,
});

chart.animate(false);
chart.data(data);

chart
.line()
.position('year*sales');

chart.annotation().line({
top: true,
start: ['min', 100],
end: ['max', 100],
style: {
stroke: 'red',
lineDash: [2, 2],
},
});

chart.annotation().regionFilter({
top: true,
start: ['min', 100],
end: ['max', 0],
color: '#f5222d'
});

chart.render();
// 防止事件内存泄露
// @ts-ignore
expect(chart.geometries[0]._events).toEqual({});

chart.changeSize(500, 400);

// @ts-ignore
expect(chart.geometries[0]._events).toEqual({});

// regionFilter 不知道怎么去断言!
})
});
2 changes: 2 additions & 0 deletions tests/bugs/sub-view-region-filter-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ describe('#0000', () => {
padding: [20, 40, 0, 30],
});

view.animate(false);

// Step 2: 载入数据源
view.data(data);
view.scale({
Expand Down
5 changes: 4 additions & 1 deletion tests/unit/component/annotation-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'jest-extended';
import { Chart } from '../../../src/';
import { COMPONENT_TYPE } from '../../../src/constant';
import { createDiv, removeDom } from '../../util/dom';
import { delay } from '../../util/delay';

const IMAGE = 'https://img.alicdn.com/tfs/TB1M.wKkND1gK0jSZFyXXciOVXa-120-120.png';

Expand Down Expand Up @@ -297,7 +298,7 @@ describe('annotation', () => {
expect(dataRegion.getElementById('-annotation-text-bg')).toBeDefined();
});

it('regionFilter', () => {
it('regionFilter', async () => {
chart.line().position('city*sale');
chart.annotation().regionFilter({
start: { city: '广州', sale: 30 },
Expand All @@ -307,6 +308,8 @@ describe('annotation', () => {
});
chart.render();

await delay(700);

const regionFilter = chart.getComponents().filter((co) => co.type === COMPONENT_TYPE.ANNOTATION)[9].component;
expect(regionFilter.get('type')).toEqual('regionFilter');
expect(regionFilter.get('shapes')).toHaveLength(1);
Expand Down
64 changes: 64 additions & 0 deletions tests/unit/geometry/base-spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import { flatten } from '@antv/util';
import 'jest-extended';
import { Chart, getEngine } from '../../../src';
import { GEOMETRY_LIFE_CIRCLE } from '../../../src/constant';
import { getCoordinate } from '../../../src/dependents';
import Geometry from '../../../src/geometry/base';
import * as Shape from '../../../src/geometry/shape/base';
import { LooseObject, ShapeInfo } from '../../../src/interface';
import { getTheme } from '../../../src/theme/';
import { createScaleByField, syncScale } from '../../../src/util/scale';
import { delay } from '../../util/delay';
import { createCanvas, createDiv, removeDom } from '../../util/dom';
import { createScale, updateScales } from '../../util/scale';

Expand Down Expand Up @@ -825,4 +827,66 @@ describe('Geometry', () => {

expect(customInfo).toEqual({ hello: 'g2' });
});

it('geometry life circle', async () => {
const data = [
{ year: '1991', value: 15468 },
{ year: '1992', value: 16100 },
{ year: '1993', value: 15900 },
{ year: '1998', value: 32040 },
];

const chart = new Chart({
container: createDiv(),
width: 500,
height: 400,
});

chart.data(data);
const geometry = chart.interval().position('year*valye');

const beforFn = jest.fn();
const afterFn = jest.fn();

// 无动画
geometry.animate(false);
geometry.once(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE, () => beforFn(1));
geometry.once(GEOMETRY_LIFE_CIRCLE.AFTER_DRAW_ANIMATE, () => afterFn(1));
chart.render();

await delay(500);

expect(beforFn).not.toBeCalled();
expect(afterFn).not.toBeCalled();

// 有动画
geometry.animate(true);
geometry.once(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE, () => beforFn(2));
geometry.once(GEOMETRY_LIFE_CIRCLE.AFTER_DRAW_ANIMATE, () => afterFn(2));
chart.changeSize(300, 300);

await delay(500);

expect(beforFn).toBeCalledWith(2);
expect(afterFn).toBeCalledWith(2);

const fn = jest.fn();
// 设置自定义动画
geometry.animate({
update: {
callback: fn,
}
});
geometry.once(GEOMETRY_LIFE_CIRCLE.BEFORE_DRAW_ANIMATE, () => beforFn(3));
geometry.once(GEOMETRY_LIFE_CIRCLE.AFTER_DRAW_ANIMATE, () => afterFn(3));
chart.changeSize(400, 400);

await delay(500);

// 自定义的 animate callback 也需要调用
expect(fn).toBeCalled();
expect(beforFn).toBeCalledWith(3);
expect(afterFn).toBeCalledWith(3);
expect(fn).toBeCalled();
});
});
3 changes: 2 additions & 1 deletion tests/unit/geometry/element/index-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Element from '../../../../src/geometry/element';
import * as Shape from '../../../../src/geometry/shape/base';
import '../../../../src/geometry/shape/interval';
import { getTheme } from '../../../../src/theme/';
import { omit } from '../../../util/omit';

const Rect = getCoordinate('rect');
const G = getEngine('canvas');
Expand Down Expand Up @@ -296,7 +297,7 @@ describe('Element', () => {
};

// @ts-ignore
expect(element.getAnimateCfg('update')).toEqual({
expect(omit(element.getAnimateCfg('update'), 'callback')).toEqual({
delay: 1000,
});
// @ts-ignore
Expand Down
11 changes: 11 additions & 0 deletions tests/util/omit.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { reduce } from '@antv/util';

export function omit(obj: any, keys: string[]): object {
// @ts-ignore
return reduce(obj, (r: any, curr: any, key: string) => {
if (!keys.includes(key)) {
r[key] = curr;
}
return r;
}, {});
}