Skip to content

Commit

Permalink
feat(region-filter): add geometry life circle, after draw animate (#2879
Browse files Browse the repository at this point in the history
)

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

* test(geometry): add test case for geometry life circle

* fix: lint error

* chore: fix test
  • Loading branch information
hustcc authored Sep 30, 2020
1 parent c851961 commit 2738acb
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 10 deletions.
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);
},
}
}
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;
}, {});
}

0 comments on commit 2738acb

Please sign in to comment.