-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(action): add action of link elements
- Loading branch information
Showing
7 changed files
with
309 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
}); | ||
}); |