diff --git a/src/interaction/action/element/link-by-color.ts b/src/interaction/action/element/link-by-color.ts index aeb6300f42..376f5e6a28 100644 --- a/src/interaction/action/element/link-by-color.ts +++ b/src/interaction/action/element/link-by-color.ts @@ -1,13 +1,16 @@ import Action from '../base'; import { getCurrentElement, getElementValue, getElementsByField } from '../util'; import Element from '../../../geometry/element/'; -import { each } from '@antv/util'; +import { deepMix, each, isFunction } from '@antv/util'; import { LooseObject } from '../../../interface'; -import { IGroup } from '../../../dependents'; +import { IGroup, ShapeAttrs } from '../../../dependents'; + +type LinkActiveStyle = ShapeAttrs | ((style: ShapeAttrs, Element: Element) => ShapeAttrs); /** * Link Elements by color - * @ignore + * + * public 方法是对外可用的反馈交互。使用方式,如:element-link-by-color:link, element-link-by-color:unlink, element-link-by-color:clear */ class LinkByColor extends Action { private linkGroup: IGroup; @@ -45,18 +48,21 @@ class LinkByColor extends Action { return path; } // 添加连接的图形 - private addLinkShape(group, element: Element, nextElement: Element) { + private addLinkShape(group: IGroup, element: Element, nextElement: Element, activeStyle?: LinkActiveStyle) { + const style = { + opacity: 0.4, + fill: element.shape.attr('fill'), + }; group.addShape({ type: 'path', attrs: { - opacity: 0.4, - fill: element.shape.attr('fill'), + ...deepMix({}, style, isFunction(activeStyle) ? activeStyle(style, element) : activeStyle), path: this.getLinkPath(element, nextElement), }, }); } // 使用图形连接 - private linkByElement(element: Element) { + private linkByElement(element: Element, activeStyle?: LinkActiveStyle) { const view = this.context.view; const scale = this.getColorScale(view, element); if (!scale) { @@ -72,7 +78,7 @@ class LinkByColor extends Action { each(elements, (el, index) => { if (index < count - 1) { const nextEl = elements[index + 1]; - this.addLinkShape(group, el, nextEl); + this.addLinkShape(group, el, nextEl, activeStyle); } }); } @@ -92,25 +98,40 @@ class LinkByColor extends Action { /** * 连接 elements + * + * @usage + * registerInteraction('xxx', { + * start: [ + * { + * trigger: 'interval:mouseenter', + * action: 'element-link-by-color:link', + * arg: { + * // style: { fill: 'red' } + * style: (style, element) => ({ fill: 'red' }) + * }, + * }, + * ], + * }); */ - public link() { + public link(args?: { style: LinkActiveStyle }) { const context = this.context; if (!this.linkGroup) { // 不允许被拾取 this.linkGroup = context.view.foregroundGroup.addGroup({ + id: 'link-by-color-group', capture: false, }); } const element = getCurrentElement(context); if (element) { - this.linkByElement(element); + this.linkByElement(element, args?.style); } } /** * 取消连接 elements */ - unlink() { + public unlink() { const element = getCurrentElement(this.context); if (element) { this.removeLink(element); @@ -120,7 +141,7 @@ class LinkByColor extends Action { /** * 清除所有连接 */ - clear() { + public clear() { if (this.linkGroup) { this.linkGroup.clear(); } diff --git a/tests/unit/interaction/action/link-by-color-spec.ts b/tests/unit/interaction/action/link-by-color-spec.ts index 903284da47..f3bee5c912 100644 --- a/tests/unit/interaction/action/link-by-color-spec.ts +++ b/tests/unit/interaction/action/link-by-color-spec.ts @@ -71,12 +71,37 @@ describe('list highlight test', () => { expect(linkGroup.getCount()).toBe(2); }); + it('custom link active-style', () => { + context.event = {}; + link.unlink(); + link.clear(); // 事先调用,保证不出错 + context.event = { + target: elements[0].shape, + }; + link.link(); + //@ts-ignore + linkGroup = link.linkGroup; + expect(linkGroup.getCount()).toBe(1); + // @ts-ignore + expect(linkGroup.getChildren()[0].getChildren()[0].attr('fill')).toBe(elements[0].shape.attr('fill')); + + link.unlink(); + link.clear(); // 事先调用,保证不出错 + context.event = { + target: elements[0].shape, + }; + link.link({ style: { fill: 'red' } }); + // @ts-ignore + expect(linkGroup.getChildren()[0].getChildren()[0].attr('fill')).toBe('red'); + }); + it('clear', () => { link.clear(); expect(linkGroup.getCount()).toBe(0); // @ts-ignore expect(link.cache).toBeEmpty(); }); + it('link null', () => { context.event = { target: null, diff --git a/tests/unit/interaction/element-link-spec.ts b/tests/unit/interaction/element-link-spec.ts new file mode 100644 index 0000000000..d3331f8a44 --- /dev/null +++ b/tests/unit/interaction/element-link-spec.ts @@ -0,0 +1,87 @@ +import { Chart, registerInteraction } from '../../../src/index'; +import { createDiv } from '../../util/dom'; + +registerInteraction('element-link', { + start: [{ trigger: 'element:mouseenter', action: 'element-link-by-color:link', arg: { style: { fill: 'red' } } }], + end: [{ trigger: 'element:mouseleave', action: 'element-link-by-color:unlink' }], +}); + +describe('test element interaction', () => { + const chart = new Chart({ + container: createDiv(), + width: 400, + height: 400, + autoFit: false, + }); + + chart.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 }, + ]); + chart.animate(true); + chart.tooltip(false); + chart.interval().position('year*sales').color('type').adjust('stack'); + chart.interaction('element-link'); + + chart.render(); + const elements = chart.geometries[0].elements; + const first = elements[0]; + + let linkGroup; + + it('active', () => { + chart.emit('element:mouseenter', { + target: first.shape, + }); + + linkGroup = chart.foregroundGroup.findById('link-by-color-group'); + expect(linkGroup).not.toBeNull(); + expect(linkGroup.getCount()).toBe(1); + + expect(linkGroup.getChildren()[0].getChildren()[0].attr('fill')).toBe('red'); + chart.emit('element:mouseleave', { + target: first.shape, + }); + expect(linkGroup.getCount()).toBe(0); + }); + + it('modify', () => { + chart.interaction('element-link', { + start: [ + { trigger: 'element:mouseenter', action: 'element-link-by-color:link', arg: { style: { fill: 'yellow' } } }, + ], + }); + chart.emit('element:mouseenter', { + target: first.shape, + }); + linkGroup = chart.foregroundGroup.findById('link-by-color-group'); + expect(linkGroup.getChildren()[0].getChildren()[0].attr('fill')).toBe('yellow'); + chart.emit('element:mouseleave', { + target: first.shape, + }); + expect(linkGroup.getCount()).toBe(0); + }); + + it('remove interaction', () => { + chart.removeInteraction('element-link'); + chart.emit('element:mouseenter', { + target: first.shape, + }); + + linkGroup = chart.foregroundGroup.findById('link-by-color-group'); + expect(linkGroup).toBeNull(); + }); + afterAll(() => { + chart.destroy(); + }); +});