diff --git a/__tests__/unit/plots/sankey/state-spec.ts b/__tests__/unit/plots/sankey/state-spec.ts
new file mode 100644
index 0000000000..e8d3fc626a
--- /dev/null
+++ b/__tests__/unit/plots/sankey/state-spec.ts
@@ -0,0 +1,65 @@
+import { Datum, Sankey } from '../../../../src';
+import { createDiv, removeDom } from '../../../utils/dom';
+import { ENERGY_RELATIONS } from '../../../data/sankey-energy';
+import { EDGES_VIEW_ID, NODES_VIEW_ID } from '../../../../src/plots/sankey/constant';
+
+describe('sankey', () => {
+ const div = createDiv();
+ const sankey = new Sankey(div, {
+ height: 500,
+ data: ENERGY_RELATIONS,
+ sourceField: 'source',
+ targetField: 'target',
+ weightField: 'value',
+ });
+
+ sankey.render();
+ it('state', () => {
+ sankey.update({
+ nodeState: {
+ active: {
+ style: {
+ fill: 'red',
+ },
+ },
+ selected: {
+ style: {
+ stroke: 'red',
+ },
+ },
+ },
+ edgeState: {
+ active: {
+ style: {
+ fill: 'blue',
+ },
+ },
+ },
+ });
+
+ sankey.setState('active', (datum: Datum) => {
+ return datum.isNode;
+ });
+
+ let elements = sankey.chart.views.find((v) => v.id === NODES_VIEW_ID).geometries[0].elements;
+ elements.forEach((element) => expect(element.shape.attr('fill')).toBe('red'));
+
+ sankey.setState('selected', (datum: Datum) => {
+ return datum.isNode;
+ });
+
+ elements = sankey.chart.views.find((v) => v.id === NODES_VIEW_ID).geometries[0].elements;
+ elements.forEach((element) => expect(element.shape.attr('stroke')).toBe('red'));
+
+ sankey.setState('active', (datum: Datum) => {
+ return !datum.isNode;
+ });
+ elements = sankey.chart.views.find((v) => v.id === EDGES_VIEW_ID).geometries[0].elements;
+ elements.forEach((element) => expect(element.shape.attr('fill')).toBe('blue'));
+ });
+
+ afterAll(() => {
+ sankey.destroy();
+ removeDom(div);
+ });
+});
diff --git a/docs/api/plots/sankey.en.md b/docs/api/plots/sankey.en.md
index 60d2ae81d2..149cc457b0 100644
--- a/docs/api/plots/sankey.en.md
+++ b/docs/api/plots/sankey.en.md
@@ -49,12 +49,28 @@ Raw fields of original data. With the 'rawsFields' definition, you can get the o
Sankey diagram node style configuration.
+#### nodeState
+
+**optional** _StyleAttr | Function_
+
+State style configuration of Sankey node.
+
+`markdown:docs/common/state-style.zh.md`
+
#### edgeStyle
**optional** _StyleAttr | Function_
Sankey diagram variable style configuration.
+#### edgeState
+
+**optional** _StyleAttr | Function_
+
+State style configuration of Sankey edge.
+
+`markdown:docs/common/state-style.zh.md`
+
`markdown:docs/common/color.en.md`
#### nodeWidthRatio
diff --git a/docs/api/plots/sankey.zh.md b/docs/api/plots/sankey.zh.md
index e7c9013ebf..e32349cc04 100644
--- a/docs/api/plots/sankey.zh.md
+++ b/docs/api/plots/sankey.zh.md
@@ -49,6 +49,14 @@ order: 27
桑基图节点样式的配置。
+#### nodeState
+
+**optional** _StyleAttr | Function_
+
+桑基图节点状态样式的配置。
+
+`markdown:docs/common/state-style.zh.md`
+
#### edgeStyle
**optional** _StyleAttr | Function_
@@ -57,6 +65,14 @@ order: 27
`markdown:docs/common/color.en.md`
+#### edgeState
+
+**optional** _StyleAttr | Function_
+
+桑基图边状态样式的配置。
+
+`markdown:docs/common/state-style.zh.md`
+
#### nodeWidthRatio
**optional** _number_
diff --git a/examples/relation-plots/sankey/demo/meta.json b/examples/relation-plots/sankey/demo/meta.json
index c24c511711..4b5df3a200 100644
--- a/examples/relation-plots/sankey/demo/meta.json
+++ b/examples/relation-plots/sankey/demo/meta.json
@@ -12,6 +12,15 @@
},
"screenshot": "https://gw.alipayobjects.com/mdn/rms_d314dd/afts/img/A*bLulSLk-VskAAAAAAAAAAAAAARQnAQ"
},
+ {
+ "filename": "set-state.ts",
+ "title": {
+ "zh": "桑基图状态交互",
+ "en": "Sankey with state"
+ },
+ "new": true,
+ "screenshot": "https://gw.alipayobjects.com/zos/antfincdn/rAuJKL%24Y6n/7a301a5e-4864-44f3-acb7-ebefb9c2402b.png"
+ },
{
"filename": "energy.ts",
"title": {
diff --git a/examples/relation-plots/sankey/demo/set-state.ts b/examples/relation-plots/sankey/demo/set-state.ts
new file mode 100644
index 0000000000..97d37f981a
--- /dev/null
+++ b/examples/relation-plots/sankey/demo/set-state.ts
@@ -0,0 +1,40 @@
+import { Sankey } from '@antv/g2plot';
+
+const DATA = [
+ { source1: '首次打开', target: '首页 UV', value: 160 },
+ { source1: '结果页', target: '首页 UV', value: 40 },
+ { source1: '验证页', target: '首页 UV', value: 10 },
+ { source1: '我的', target: '首页 UV', value: 10 },
+ { source1: '朋友', target: '首页 UV', value: 8 },
+ { source1: '其他来源', target: '首页 UV', value: 27 },
+ { source1: '首页 UV', target: '理财', value: 30 },
+ { source1: '首页 UV', target: '扫一扫', value: 40 },
+ { source1: '首页 UV', target: '服务', value: 35 },
+ { source1: '首页 UV', target: '蚂蚁森林', value: 25 },
+ { source1: '首页 UV', target: '跳失', value: 10 },
+ { source1: '首页 UV', target: '借呗', value: 30 },
+ { source1: '首页 UV', target: '花呗', value: 40 },
+ { source1: '首页 UV', target: '其他流向', value: 45 },
+];
+
+const sankey = new Sankey('container', {
+ data: DATA,
+ sourceField: 'source1',
+ targetField: 'target',
+ weightField: 'value',
+ nodeWidthRatio: 0.018,
+ nodePaddingRatio: 0.03,
+ nodeState: {
+ active: {
+ style: {
+ linewidth: 1.5
+ }
+ }
+ },
+ tooltip: { showTitle: true }
+});
+
+sankey.render();
+sankey.setState('active', (datum) => {
+ return datum.isNode && datum.name === '首次打开'
+});
diff --git a/src/plots/sankey/adaptor.ts b/src/plots/sankey/adaptor.ts
index 3a6000684e..466b949e63 100644
--- a/src/plots/sankey/adaptor.ts
+++ b/src/plots/sankey/adaptor.ts
@@ -37,7 +37,7 @@ function defaultOptions(params: Params): Params {
*/
function geometry(params: Params): Params {
const { chart, options } = params;
- const { color, nodeStyle, edgeStyle, label, tooltip } = options;
+ const { color, nodeStyle, edgeStyle, label, tooltip, nodeState, edgeState } = options;
// 1. 组件,优先设置,因为子 view 会继承配置
chart.legend(false);
@@ -67,14 +67,7 @@ function geometry(params: Params): Params {
shape: 'arc',
},
tooltip,
- state: {
- active: {
- style: {
- opacity: 0.8,
- lineWidth: 0,
- },
- },
- },
+ state: edgeState,
},
});
@@ -93,6 +86,7 @@ function geometry(params: Params): Params {
},
label,
tooltip,
+ state: nodeState,
},
});
diff --git a/src/plots/sankey/index.ts b/src/plots/sankey/index.ts
index 16660b68d2..1352eacfd4 100644
--- a/src/plots/sankey/index.ts
+++ b/src/plots/sankey/index.ts
@@ -1,8 +1,9 @@
-import { get } from '@antv/util';
+import { get, each } from '@antv/util';
+import { Element } from '@antv/g2';
import { Plot } from '../../core/plot';
import { Adaptor } from '../../core/adaptor';
-import { Data, Datum } from '../../types';
-import { findViewById } from '../../utils';
+import { Data, Datum, StateCondition, StateName, StateObject } from '../../types';
+import { findViewById, getAllElementsRecursively } from '../../utils';
import { SankeyOptions } from './types';
import { adaptor } from './adaptor';
import { transformToViewsData } from './helper';
@@ -32,6 +33,14 @@ export class Sankey extends Plot {
opacity: 0.3,
lineWidth: 0,
},
+ edgeState: {
+ active: {
+ style: {
+ opacity: 0.8,
+ lineWidth: 0,
+ },
+ },
+ },
label: {
formatter: ({ name }) => name,
callback: (x: number[]) => {
@@ -95,6 +104,40 @@ export class Sankey extends Plot {
edgesView.changeData(edges);
}
+ /**
+ * 设置状态
+ * @param type 状态类型,支持 'active' | 'inactive' | 'selected' 三种
+ * @param conditions 条件,支持数组
+ * @param status 是否激活,默认 true
+ */
+ public setState(type: StateName, condition: StateCondition, status: boolean = true) {
+ const elements = getAllElementsRecursively(this.chart);
+
+ each(elements, (ele: Element) => {
+ if (condition(ele.getData())) {
+ ele.setState(type, status);
+ }
+ });
+ }
+
+ /**
+ * 获取状态
+ */
+ public getStates(): StateObject[] {
+ const elements = getAllElementsRecursively(this.chart);
+
+ const stateObjects: StateObject[] = [];
+ each(elements, (element: Element) => {
+ const data = element.getData();
+ const states = element.getStates();
+ each(states, (state) => {
+ stateObjects.push({ data, state, geometry: element.geometry, element });
+ });
+ });
+
+ return stateObjects;
+ }
+
/**
* 获取适配器
*/
diff --git a/src/plots/sankey/types.ts b/src/plots/sankey/types.ts
index 0766542482..75ad071592 100644
--- a/src/plots/sankey/types.ts
+++ b/src/plots/sankey/types.ts
@@ -1,4 +1,4 @@
-import { Data, Options, StyleAttr } from '../../types';
+import { Data, Options, State, StyleAttr } from '../../types';
import { NodeDepth, NodeSort } from './layout';
/** 配置类型定义 */
@@ -55,10 +55,18 @@ export interface SankeyOptions extends Omit