Skip to content

Commit

Permalink
feat(action): add action of link elements
Browse files Browse the repository at this point in the history
  • Loading branch information
dxq613 authored and simaQ committed Mar 9, 2020
1 parent d9f45f7 commit 67c66d4
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 1 deletion.
55 changes: 55 additions & 0 deletions examples/interaction/element/demo/element-link.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { Chart, registerInteraction } from '@antv/g2';

registerInteraction('element-link', {
start: [
{trigger: 'interval:mouseenter', action: 'element-link-by-color:link'}
],
end: [
{trigger: 'interval:mouseleave', action: 'element-link-by-color:unlink'}
]
});

const data = [
{ year: '2014', type: 'Sales', sales: 1000 },
{ year: '2015', type: 'Sales', sales: 1170 },
{ year: '2016', type: 'Sales', sales: 660 },
{ year: '2017', type: 'Sales', sales: 1030 },
{ year: '2014', type: 'Expenses', sales: 400 },
{ year: '2015', type: 'Expenses', sales: 460 },
{ year: '2016', type: 'Expenses', sales: 1120 },
{ year: '2017', type: 'Expenses', sales: 540 },
{ year: '2014', type: 'Profit', sales: 300 },
{ year: '2015', type: 'Profit', sales: 300 },
{ year: '2016', type: 'Profit', sales: 300 },
{ year: '2017', type: 'Profit', sales: 350 },
];

const chart = new Chart({
container: 'container',
autoFit: true,
height: 500,
});

chart.data(data);
chart.scale({
sales: {
max: 2400,
tickInterval: 600,
nice: true,
},
});

chart.tooltip({
showMarkers: false
});

chart
.interval()
.position('year*sales')
.color('type')
.adjust('stack');

chart.interaction('element-highlight-by-color');
chart.interaction('element-link');

chart.render();
8 changes: 8 additions & 0 deletions examples/interaction/element/demo/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f5c722/afts/img/A*1PZ-QI-g7j0AAAAAAAAAAABkARQnAQ"
},
{
"filename": "element-link.ts",
"title": {
"zh": "高亮所有同颜色的 element,并连接相同颜色的图形",
"en": "element highlight and linked by color"
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_f5c722/afts/img/A*1T85TpUoCPoAAAAAAAAAAABkARQnAQ"
},
{
"filename": "list-active.ts",
"title": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"clean": "rimraf lib esm dist",
"lint": "lint-staged",
"test": "jest",
"test-live": "DEBUG_MODE=1 jest --watch tests/unit",
"test-live": "DEBUG_MODE=1 jest --watch tests/unit/interaction/action/link-by-color-spec.ts",
"coverage": "jest --coverage",
"lib:cjs": "tsc -p tsconfig.json --target ES5 --module commonjs --outDir lib",
"lib:esm": "tsc -p tsconfig.json --target ES5 --module ESNext --outDir esm",
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ import TooltipAction from './interaction/action/component/tooltip';
import ElmentActive from './interaction/action/element/active';
import ElmentRangeActive from './interaction/action/element/range-active';
import ElmentSingleActive from './interaction/action/element/single-active';
import ElementLinkByColor from './interaction/action/element/link-by-color';

import ElmentHighlight from './interaction/action/element/highlight';
import ElmentHighlightByColor from './interaction/action/element/highlight-by-color';
Expand Down Expand Up @@ -209,6 +210,7 @@ registerAction('element-sibling-highlight', ElmentRangeHighlight, {
registerAction('element-selected', ElementSelected);
registerAction('element-single-selected', ElementSingleSelected);
registerAction('element-range-selected', ElementRangeSelected);
registerAction('element-link-by-color', ElementLinkByColor);

registerAction('active-region', ActiveRegion);
registerAction('list-active', ListActive);
Expand Down
128 changes: 128 additions & 0 deletions src/interaction/action/element/link-by-color.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import Action from '../base';
import { getCurrentElement, getElementValue, getElementsByField } from '../util';
import Element from '../../../geometry/element/';
import { each } from '@antv/util';
import { LooseObject, IGroup } from '@antv/g-base';

/**
* Link Elements by color
* @ignore
*/
class LinkByColor extends Action {
private linkGroup: IGroup;
private cache: LooseObject = {};
// 获取颜色对应的 scale
private getColorScale(view, element) {
const colorAttr = element.geometry.getAttribute('color');
if (!colorAttr) {
return null;
}
const scale = view.getScaleByField(colorAttr.getFields()[0]);
return scale;
}
// 获取连接的 path
private getLinkPath(element: Element, nextElement: Element) {
const bbox = element.shape.getCanvasBBox();
const nextBBox= nextElement.shape.getCanvasBBox();
const path = [
['M', bbox.maxX, bbox.minY],
['L', nextBBox.minX, nextBBox.minY],
['L', nextBBox.minX, nextBBox.maxY],
['L', bbox.maxX, bbox.maxY],
['Z']
]
return path;
}
// 添加连接的图形
private addLinkShape(group, element: Element, nextElement: Element) {
group.addShape({
type: 'path',
attrs: {
opacity: 0.4,
fill: element.shape.attr('fill'),
path: this.getLinkPath(element, nextElement)
}
});
}
// 使用图形连接
private linkByElement(element: Element) {
const view = this.context.view;
const scale = this.getColorScale(view, element);
if (!scale) {
return;
}
const value = getElementValue(element, scale.field);
if (!this.cache[value]) {
const elements = getElementsByField(view, scale.field, value);
const linkGroup = this.linkGroup;
const group = linkGroup.addGroup();
this.cache[value] = group; // 缓存
const count = elements.length;
each(elements, (el, index) => {
if (index < count -1) {
const nextEl = elements[index + 1];
this.addLinkShape(group, el, nextEl);
}
});
}
}
// 移除连接
private removeLink(element) {
const scale = this.getColorScale(this.context.view, element);
if (!scale) {
return;
}
const value = getElementValue(element, scale.field);
if (this.cache[value]) {
this.cache[value].remove();
this.cache[value] = null;
}
}

/**
* 连接 elements
*/
public link() {
const context = this.context;
if (!this.linkGroup) {
// 不允许被拾取
this.linkGroup = context.view.foregroundGroup.addGroup({
capture: false
});
}
const element = getCurrentElement(context);
if (element) {
this.linkByElement(element);
}
}

/**
* 取消连接 elements
*/
unlink() {
const element = getCurrentElement(this.context);
if (element) {
this.removeLink(element);
}
}

/**
* 清除所有连接
*/
clear() {
if (this.linkGroup) {
this.linkGroup.clear();
}
}

/**
* 销毁
*/
destroy() {
super.destroy();
if(this.linkGroup) {
this.linkGroup.remove();
}
}
}
export default LinkByColor;
14 changes: 14 additions & 0 deletions src/interaction/action/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,20 @@ export function getElements(view: View): Element[] {
return rst;
}

/**
* 获取所有的图表元素
* @param view View/Chart
* @param field 字段名
* @param value 字段值
* @ignore
*/
export function getElementsByField(view: View, field: string, value: any) {
const elements = getElements(view);
return elements.filter(el => {
return getElementValue(el, field) === value;
});
}

/**
* 根据状态名获取图表元素
* @param view View/Chart
Expand Down
101 changes: 101 additions & 0 deletions tests/unit/interaction/action/link-by-color-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { Chart } from '../../../../src/index';
import Link from '../../../../src/interaction/action/element/link-by-color';
import Context from '../../../../src/interaction/context';
import { createDiv } from '../../../util/dom';

describe('list highlight test', () => {
const chart = new Chart({
container: createDiv(),
width: 400,
height: 400,
autoFit: false,
});
chart.data([
{ year: '1991', value: 13, type: '1' },
{ year: '1992', value: 34 , type: '1'},
{ year: '1993', value: 5, type: '1' },
{ year: '1994', value: 34, type: '1' },

{ year: '1991', value: 13, type: '2' },
{ year: '1992', value: 34, type: '2' },
{ year: '1993', value: 5, type: '2' },
{ year: '1994', value: 34, type: '2' },
]);
chart.animate(false);
chart.tooltip(false);
const interval = chart
.interval()
.position('year*value')
.color('type')
.adjust('stack');
chart.render();
const context = new Context(chart);
const link = new Link(context);
const elements = interval.elements;
let linkGroup;
it('link', () => {
context.event = {

};
link.unlink();
link.clear(); // 事先调用,保证不出错
context.event = {
target: elements[0].shape
};
link.link();
//@ts-ignore
linkGroup = link.linkGroup;
expect(linkGroup.getCount()).toBe(1);
});

it('unlink', () => {
context.event = {
target: null
};
link.unlink();
expect(linkGroup.getCount()).toBe(1);
context.event = {
target: elements[0].shape
};
link.unlink();
expect(linkGroup.getCount()).toBe(0);
});

it('multiple link', () => {
context.event = {
target: elements[0].shape
};
link.link();

context.event = {
target: elements[4].shape
};
link.link();
expect(linkGroup.getCount()).toBe(2);
});

it('clear', () => {
link.clear();
expect(linkGroup.getCount()).toBe(0);
});
it('link null', () => {
context.event = {
target: null
};
expect(linkGroup.getCount()).toBe(0);
});
it('no color', () => {
chart.clear();
const interval1 = chart
.interval()
.position('year*value');

chart.render();
context.event = {
target: interval1.elements[0].shape
};
link.link();
expect(linkGroup.getCount()).toBe(0);
link.unlink();
});
});

0 comments on commit 67c66d4

Please sign in to comment.