Skip to content

Commit

Permalink
fix(interaction): 添加 venn图 selected 和 active 的交互 (#2871)
Browse files Browse the repository at this point in the history
* fix(interaction): 添加venn图的selected和active的交互

* test(venn): 添加 venn 图交互的单测和文档

* refactor(venn): 规范 venn interaction 的写法

Co-authored-by: 酥云 <[email protected]>
  • Loading branch information
Angelii and 酥云 authored Sep 26, 2021
1 parent 626a50c commit bf4b248
Show file tree
Hide file tree
Showing 8 changed files with 298 additions and 2 deletions.
151 changes: 151 additions & 0 deletions __tests__/unit/plots/venn/interaction-spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import { IGroup } from '@antv/g-base';
import InteractionContext from '@antv/g2/lib/interaction/context';
import { Venn } from '../../../../src';
import { VennElementActive, VennElementSelected } from '../../../../src/plots/venn/interaction/action';
import { createDiv } from '../../../utils/dom';

describe('venn', () => {
const data = [
{ sets: ['A'], size: 12, label: 'A' },
{ sets: ['B'], size: 12, label: 'B' },
{ sets: ['A', 'B'], size: 2, label: 'A&B' },
];

const plot = new Venn(createDiv(), {
data,
width: 400,
height: 500,
setsField: 'sets',
sizeField: 'size',
legend: false,
pointStyle: {
lineWidth: 0,
stroke: 'black',
},
});
plot.render();

it('venn: active', () => {
plot.update({
state: {
active: {
style: {
lineWidth: 1,
},
},
},
interactions: [{ type: 'venn-element-active', enable: true }],
});

const context = new InteractionContext(plot.chart);
const vennElementActive = new VennElementActive(context);

// 模拟 active
context.event = {
data: {
data: plot.chart.getData()[0],
},
};
vennElementActive.active();

const elements = plot.chart.geometries[0].elements;

expect(plot.getStates().length).toBe(1);
expect(plot.getStates()[0].state).toBe('active');
expect(elements[0].getStates()[0]).toBe('active');
expect((elements[0].shape as IGroup).getChildren()[0].attr('lineWidth')).toBe(1);

vennElementActive.reset();
expect(plot.getStates().length).toBe(0);

// 模拟 第二次 active
context.event = {
data: {
data: plot.chart.getData()[1],
},
};
vennElementActive.active();

expect(plot.getStates().length).toBe(1);
expect(plot.getStates()[0].state).toBe('active');
// 第一个元素 样式恢复
expect(elements[0].getStates()[0]).toBeUndefined();
expect((elements[0].shape as IGroup).getChildren()[0].attr('lineWidth')).toBe(0);

// 第二个元素 有样式
expect(elements[1].getStates()[0]).toBe('active');
expect((elements[1].shape as IGroup).getChildren()[0].attr('lineWidth')).toBe(1);

vennElementActive.reset();
expect(plot.getStates().length).toBe(0);

vennElementActive.destroy();
});

it('venn: selected', () => {
plot.update({
state: {
selected: {
style: {
lineWidth: 2,
},
},
},
interactions: [
{ type: 'venn-element-active', enable: false },
{ type: 'venn-element-selected', enable: true },
],
});

const context = new InteractionContext(plot.chart);
const vennElementSelected = new VennElementSelected(context);

// 模拟 selected
context.event = {
data: {
data: plot.chart.getData()[0],
},
};
vennElementSelected.toggle();

const elements = plot.chart.geometries[0].elements;

// 第一个元素 点击 有样式
expect(plot.getStates().length).toBe(1);
expect(plot.getStates()[0].state).toBe('selected');
expect(elements[0].getStates()[0]).toBe('selected');
expect((elements[0].shape as IGroup).getChildren()[0].attr('lineWidth')).toBe(2);

// 再次点击 取消 selected
vennElementSelected.toggle();
expect(plot.getStates().length).toBe(0);
expect(elements[0].getStates()[0]).toBeUndefined();
expect((elements[0].shape as IGroup).getChildren()[0].attr('lineWidth')).toBe(0);

// 模拟第二个元素的 selected
context.event = {
data: {
data: plot.chart.getData()[1],
},
};
vennElementSelected.toggle();

expect(plot.getStates().length).toBe(1);
expect(plot.getStates()[0].state).toBe('selected');
expect(elements[1].getStates()[0]).toBe('selected');
expect((elements[1].shape as IGroup).getChildren()[0].attr('lineWidth')).toBe(2);

// 所有元素的 selected state 为 false
vennElementSelected.reset();
expect(plot.getStates().length).toBe(0);
expect(elements[0].getStates().length).toBe(0);
expect(elements[1].getStates().length).toBe(0);
expect(elements[2].getStates().length).toBe(0);

vennElementSelected.destroy();
});

afterAll(() => {
plot.destroy();
});
});
9 changes: 8 additions & 1 deletion docs/api/plots/venn.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ The name of the data field corresponding to the point size map.

<description>**optional** _string_</description>

Color blend mode of the intersection area, default: `multiply`. Other: `darken`, `lighten`, `screen`, `overlay`, `burn`, and `dodge`.
Color blend mode of the intersection area, default: `multiply`. Other: `normal`, `darken`, `lighten`, `screen`, `overlay`, `burn`, and `dodge`.
reference:https://gka.github.io/chroma.js/#chroma-blend

#### pointStyle
Expand Down Expand Up @@ -110,6 +110,13 @@ Default configuration:

### Plot Interactions

There are interactions for venn diagrams, listed below:

| interaction | description | configuration method |
| ---|--|--|
| venn-element-active | enable the "mouse-over venn diagram element triggers active" interaction | `interactions:[{ type: 'venn-element-active', enabled: true }]` |
| venn-element-selected | enable the interaction "trigger selected when mouse clicked on venn diagram element", multiple options available | `interactions:[{ type: 'venn-element-selected', enabled: true }]` |

`markdown:docs/common/interactions.en.md`

### Plot Event
Expand Down
9 changes: 8 additions & 1 deletion docs/api/plots/venn.zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ order: 12

<description>**optional** _string_</description>

交集区域的颜色混合方式, 默认: `multiply`(正片叠底)。可选项: `multiply`, `darken`, `lighten`, `screen`, `overlay`, `burn`, and `dodge`.
交集区域的颜色混合方式, 默认: `multiply`(正片叠底)。可选项: `multiply`, `normal`, `darken`, `lighten`, `screen`, `overlay`, `burn`, and `dodge`.
参考:https://gka.github.io/chroma.js/#chroma-blend

#### pointStyle
Expand Down Expand Up @@ -110,6 +110,13 @@ order: 12

### 图表交互

内置了针对 venn 图交互,列表如下:

| 交互 | 描述 | 配置方式 |
| ---|---|---|
| venn-element-active | 开启「鼠标移入 venn 图元素时触发 active」的交互 | `interactions:[{ type: 'venn-element-active', enabled: true }]` |
| venn-element-selected | 开启「鼠标点击 venn 图元素时触发 selected」的交互,可多选 | `interactions:[{ type: 'venn-element-selected', enabled: true }]` |

`markdown:docs/common/interactions.zh.md`

### 图表事件
Expand Down
37 changes: 37 additions & 0 deletions examples/more-plots/venn/demo/interaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { Venn } from '@antv/g2plot';

const plot = new Venn('container', {
data: [
{ sets: ['A'], size: 12, label: 'A' },
{ sets: ['B'], size: 12, label: 'B' },
{ sets: ['C'], size: 12, label: 'C' },
{ sets: ['A', 'B'], size: 2, label: 'A&B' },
{ sets: ['A', 'C'], size: 2, label: 'A&C' },
{ sets: ['B', 'C'], size: 2, label: 'B&C' },
{ sets: ['A', 'B', 'C'], size: 1 },
],
setsField: 'sets',
sizeField: 'size',
pointStyle: { fillOpacity: 0.8 },
padding: [0, 10],
state: {
active: {
style: {
fillOpacity: 1,
stroke: 'black',
lineWidth: 1,
},
},
selected: {
style: {
stroke: 'black',
lineWidth: 2,
},
},
},
interactions: [
{ type: 'venn-element-active', enable: true },
{ type: 'venn-element-selected', enable: true },
],
});
plot.render();
9 changes: 9 additions & 0 deletions examples/more-plots/venn/demo/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@
},
"new": true,
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/T6cgHx5BHB/f2137e3b-5784-4626-a986-109fc8cb5feb.png"
},
{
"filename": "interaction.ts",
"title": {
"zh": "韦恩图 - 元素交互",
"en": "venn plot - with element action"
},
"new": true,
"screenshot": "https://gw.alipayobjects.com/zos/antfincdn/x%24Jxds2L2T/0ff5000b-d23e-49a8-a02a-4eb02d7a8e47.png"
}
]
}
1 change: 1 addition & 0 deletions src/plots/venn/adaptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CustomInfo, VennData, VennOptions } from './types';
import { ID_FIELD } from './constant';
import './shape';
import './label';
import './interaction';

/** 图例默认预留空间 */
export const LEGEND_SPACE = 40;
Expand Down
67 changes: 67 additions & 0 deletions src/plots/venn/interaction/action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { InteractionAction } from '@antv/g2';

class VennElementState extends InteractionAction {
/** tofront: 同步所有元素的位置 */
protected syncElementsPos() {
const elements = this.context.view.geometries[0].elements;
elements.forEach((elem) => {
elem.shape.toFront();
});
}
}

export class VennElementActive extends VennElementState {
/** hover */
public active() {
const { data } = this.context.event.data;
const elements = this.context.view.geometries[0].elements;

elements.forEach((elem) => {
const activeState = data === elem.getData();
elem.setState('active', activeState);
});
// tofront: 同步所有元素的位置
this.syncElementsPos();
}

/** 重置 */
public reset() {
const elements = this.context.view.geometries[0].elements;

elements.forEach((elem) => {
// 所有元素的 state 统一 false
elem.setState('active', false);
});
// tofront: 同步所有元素的位置
this.syncElementsPos();
}
}

export class VennElementSelected extends VennElementState {
/** 切换 */
public toggle() {
const { data } = this.context.event.data;
const elements = this.context.view.geometries[0].elements;

elements.forEach((elem) => {
if (data === elem.getData()) {
const selectedState = elem.getStates().includes('selected');
elem.setState('selected', !selectedState);
}
});
// tofront: 同步所有元素的位置
this.syncElementsPos();
}

/** 重置 */
public reset() {
const elements = this.context.view.geometries[0].elements;

elements.forEach((elem) => {
// 所有元素的 state 统一 false
elem.setState('selected', false);
});
// tofront: 同步所有元素的位置
this.syncElementsPos();
}
}
17 changes: 17 additions & 0 deletions src/plots/venn/interaction/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { registerInteraction, registerAction } from '@antv/g2';
import { VennElementActive, VennElementSelected } from './action';

registerAction('venn-element-active', VennElementActive);
registerAction('venn-element-selected', VennElementSelected);

// 移动到 venn elment 上的 active
registerInteraction('venn-element-active', {
start: [{ trigger: 'element:mouseenter', action: 'venn-element-active:active' }],
end: [{ trigger: 'element:mouseleave', action: 'venn-element-active:reset' }],
});

// 点击 venn element (可多选)
registerInteraction('venn-element-selected', {
start: [{ trigger: 'element:click', action: 'venn-element-selected:toggle' }],
rollback: [{ trigger: 'dblclick', action: ['venn-element-selected:reset'] }],
});

0 comments on commit bf4b248

Please sign in to comment.