Skip to content

Commit 947889b

Browse files
authored
feat(sankey): add nodeWidth, nodePadding options (#2295)
* feat(sankey): add nodeWidth, nodePadding options * test: fix ci by node edge order
1 parent 3ff9ecc commit 947889b

File tree

8 files changed

+104
-33
lines changed

8 files changed

+104
-33
lines changed

__tests__/bugs/issue-2267-spec.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Sankey } from '../../src';
2+
import { createDiv } from '../utils/dom';
3+
import { ALIPAY_DATA } from '../data/sankey-energy';
4+
5+
describe('#2267', () => {
6+
const sankey = new Sankey(createDiv(), {
7+
data: ALIPAY_DATA,
8+
sourceField: 'source',
9+
targetField: 'target',
10+
weightField: 'value',
11+
nodeWidth: 16,
12+
nodePadding: 10,
13+
});
14+
15+
sankey.render();
16+
17+
it('sankey nodeWidth, nodePadding px configure', () => {
18+
const width = sankey.chart.views[1].geometries[0].container.getChildren()[0].getBBox().width;
19+
// 一定的误差
20+
expect(width > 12 && width < 20).toBe(true);
21+
});
22+
});

__tests__/data/sankey-energy.ts

+17
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,20 @@ export const ENERGY_RELATIONS = [
235235
{ source: 'Wave', target: 'Electricity grid', value: 19.013 },
236236
{ source: 'Wind', target: 'Electricity grid', value: 289.366 },
237237
];
238+
239+
export const ALIPAY_DATA = [
240+
{ source: '首次打开', target: '首页 UV', value: 160 },
241+
{ source: '结果页', target: '首页 UV', value: 40 },
242+
{ source: '验证页', target: '首页 UV', value: 10 },
243+
{ source: '我的', target: '首页 UV', value: 10 },
244+
{ source: '朋友', target: '首页 UV', value: 8 },
245+
{ source: '其他来源', target: '首页 UV', value: 27 },
246+
{ source: '首页 UV', target: '理财', value: 30 },
247+
{ source: '首页 UV', target: '扫一扫', value: 40 },
248+
{ source: '首页 UV', target: '服务', value: 35 },
249+
{ source: '首页 UV', target: '蚂蚁森林', value: 25 },
250+
{ source: '首页 UV', target: '跳失', value: 10 },
251+
{ source: '首页 UV', target: '借呗', value: 30 },
252+
{ source: '首页 UV', target: '花呗', value: 40 },
253+
{ source: '首页 UV', target: '其他流向', value: 45 },
254+
];

__tests__/unit/plots/sankey/index-spec.ts

+14-12
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,20 @@ describe('sankey', () => {
3131
expect(sankey.options.appendPadding).toEqual(8);
3232

3333
// node
34-
expect(sankey.chart.views[0].geometries[0].type).toBe('polygon');
35-
expect(sankey.chart.views[0].geometries[0].data.length).toBe(48);
36-
expect(sankey.chart.views[0].geometries[0].data[0]).toEqual({
34+
expect(sankey.chart.views[1].geometries[0].type).toBe('polygon');
35+
expect(sankey.chart.views[1].geometries[0].data.length).toBe(48);
36+
expect(sankey.chart.views[1].geometries[0].data[0]).toEqual({
37+
isNode: true,
3738
name: "Agricultural 'waste'",
3839
x: [0, 0.008, 0.008, 0],
3940
y: [0.26075939300940637, 0.26075939300940637, 0.2963247055394385, 0.2963247055394385],
4041
});
4142

4243
// edge
43-
expect(sankey.chart.views[1].geometries[0].type).toBe('edge');
44-
expect(sankey.chart.views[1].geometries[0].data.length).toBe(68);
45-
expect(sankey.chart.views[1].geometries[0].data[0]).toEqual({
44+
expect(sankey.chart.views[0].geometries[0].type).toBe('edge');
45+
expect(sankey.chart.views[0].geometries[0].data.length).toBe(68);
46+
expect(sankey.chart.views[0].geometries[0].data[0]).toEqual({
47+
isNode: false,
4648
name: "Agricultural 'waste'",
4749
source: "Agricultural 'waste'",
4850
target: 'Bio-conversion',
@@ -52,11 +54,11 @@ describe('sankey', () => {
5254
});
5355

5456
// label
55-
expect(sankey.chart.views[0].geometries[0].labelsContainer.getChildren().length).toBe(48);
56-
expect(sankey.chart.views[0].geometries[0].labelsContainer.getChildByIndex(0).cfg.children[0].attr('text')).toBe(
57+
expect(sankey.chart.views[1].geometries[0].labelsContainer.getChildren().length).toBe(48);
58+
expect(sankey.chart.views[1].geometries[0].labelsContainer.getChildByIndex(0).cfg.children[0].attr('text')).toBe(
5759
"Agricultural 'waste'"
5860
);
59-
expect(sankey.chart.views[1].geometries[0].labelsContainer.getChildren().length).toBe(0);
61+
expect(sankey.chart.views[0].geometries[0].labelsContainer.getChildren().length).toBe(0);
6062

6163
// tooltip
6264
sankey.chart.showTooltip({ x: 100, y: 100 });
@@ -105,15 +107,15 @@ describe('sankey', () => {
105107
sankey.render();
106108

107109
// @ts-ignore
108-
expect(sankey.chart.views[1].geometries[0].styleOption.cfg).toEqual({
110+
expect(sankey.chart.views[0].geometries[0].styleOption.cfg).toEqual({
109111
fill: '#ccc',
110112
fillOpacity: 0.5,
111113
lineWidth: 0,
112114
opacity: 0.3,
113115
});
114116

115117
// @ts-ignore
116-
expect(sankey.chart.views[0].geometries[0].styleOption.fields).toEqual(['x', 'y', 'name']);
118+
expect(sankey.chart.views[1].geometries[0].styleOption.fields).toEqual(['x', 'y', 'name']);
117119

118120
expect(d).toEqual({
119121
name: '其他流向',
@@ -142,7 +144,7 @@ describe('sankey', () => {
142144
sankey.render();
143145

144146
// 被去掉环
145-
expect(sankey.chart.views[1].getOptions().data.length).toBe(3);
147+
expect(sankey.chart.views[0].getOptions().data.length).toBe(3);
146148

147149
sankey.destroy();
148150
});

src/plots/sankey/adaptor.ts

+27-19
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { transformDataToNodeLinkData } from '../../utils/data';
77
import { SankeyOptions } from './types';
88
import { X_FIELD, Y_FIELD, COLOR_FIELD } from './constant';
99
import { cutoffCircle } from './circle';
10+
import { getNodePaddingRatio, getNodeWidthRatio } from './helper';
1011

1112
/**
1213
* geometry 处理
@@ -26,7 +27,9 @@ function geometry(params: Params<SankeyOptions>): Params<SankeyOptions> {
2627
tooltip,
2728
nodeAlign,
2829
nodePaddingRatio,
30+
nodePadding,
2931
nodeWidthRatio,
32+
nodeWidth,
3033
nodeSort,
3134
} = options;
3235

@@ -47,8 +50,10 @@ function geometry(params: Params<SankeyOptions>): Params<SankeyOptions> {
4750
const { nodes, links } = sankeyLayout(
4851
{
4952
nodeAlign,
50-
nodePadding: nodePaddingRatio,
51-
nodeWidth: nodeWidthRatio,
53+
// @ts-ignore
54+
nodePadding: getNodePaddingRatio(nodePadding, nodePaddingRatio, chart.height),
55+
// @ts-ignore
56+
nodeWidth: getNodeWidthRatio(nodeWidth, nodeWidthRatio, chart.width),
5257
nodeSort,
5358
},
5459
sankeyLayoutInputData
@@ -60,6 +65,7 @@ function geometry(params: Params<SankeyOptions>): Params<SankeyOptions> {
6065
x: node.x,
6166
y: node.y,
6267
name: node.name,
68+
isNode: true,
6369
};
6470
});
6571
const edgesData = links.map((link) => {
@@ -70,27 +76,11 @@ function geometry(params: Params<SankeyOptions>): Params<SankeyOptions> {
7076
x: link.x,
7177
y: link.y,
7278
value: link.value,
79+
isNode: false,
7380
};
7481
});
7582

7683
// 5. node edge views
77-
const nodeView = chart.createView();
78-
nodeView.data(nodesData);
79-
80-
polygon({
81-
chart: nodeView,
82-
options: {
83-
xField: X_FIELD,
84-
yField: Y_FIELD,
85-
seriesField: COLOR_FIELD,
86-
polygon: {
87-
color,
88-
style: nodeStyle,
89-
},
90-
label,
91-
tooltip: false,
92-
},
93-
});
9484

9585
// edge view
9686
const edgeView = chart.createView();
@@ -120,6 +110,24 @@ function geometry(params: Params<SankeyOptions>): Params<SankeyOptions> {
120110
},
121111
});
122112

113+
const nodeView = chart.createView();
114+
nodeView.data(nodesData);
115+
116+
polygon({
117+
chart: nodeView,
118+
options: {
119+
xField: X_FIELD,
120+
yField: Y_FIELD,
121+
seriesField: COLOR_FIELD,
122+
polygon: {
123+
color,
124+
style: nodeStyle,
125+
},
126+
label,
127+
tooltip,
128+
},
129+
});
130+
123131
chart.interaction('element-active');
124132

125133
// scale

src/plots/sankey/helper.ts

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { isRealNumber } from '../../utils/number';
2+
3+
export function getNodeWidthRatio(nodeWidth: number, nodeWidthRatio: number, width: number) {
4+
return isRealNumber(nodeWidth) ? nodeWidth / width : nodeWidthRatio;
5+
}
6+
7+
export function getNodePaddingRatio(nodePadding: number, nodePaddingRatio: number, height: number) {
8+
return isRealNumber(nodePadding) ? nodePadding / height : nodePaddingRatio;
9+
}

src/plots/sankey/index.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { get } from '@antv/util';
12
import { Plot } from '../../core/plot';
23
import { Adaptor } from '../../core/adaptor';
34
import { Datum } from '../../types';
@@ -48,7 +49,12 @@ export class Sankey extends Plot<SankeyOptions> {
4849
tooltip: {
4950
showTitle: false,
5051
showMarkers: false,
51-
fields: ['source', 'target', 'value'],
52+
shared: false,
53+
fields: ['name', 'source', 'target', 'value', 'isNode'],
54+
// 内置:node 不显示 tooltip,edge 显示 tooltip
55+
showContent: (items) => {
56+
return !get(items, [0, 'data', 'isNode']);
57+
},
5258
formatter: (datum: Datum) => {
5359
const { source, target, value } = datum;
5460
return {

src/plots/sankey/types.ts

+8
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,18 @@ export interface SankeyOptions extends Omit<Options, 'xField' | 'yField' | 'xAxi
2222
* 节点宽度的比如,参考画布的宽度,默认值为 0.008
2323
*/
2424
readonly nodeWidthRatio?: number;
25+
/**
26+
* 节点宽度的像素设置,优先级高于 nodeWidthRatio
27+
*/
28+
readonly nodeWidth?: number;
2529
/**
2630
* 节点之间的间距比例,参考画布高度,默认值为 0.03
2731
*/
2832
readonly nodePaddingRatio?: number;
33+
/**
34+
* 节点间距的像素设置,优先级高于 nodePaddingRatio
35+
*/
36+
readonly nodePadding?: number;
2937
/**
3038
* 节点对齐的方式,默认为 justify
3139
*/

src/plots/treemap/interactions/actions/treemap-drill-down-action.ts

-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ export class TreemapDrillDownAction extends Action {
77
public cacheDataStack: Record<string, any>[][] = [];
88

99
drill(data) {
10-
console.log(this.context);
1110
const { view } = this.context;
1211
const currentData = view.getData();
1312
const groupScales = view.getGroupScales();

0 commit comments

Comments
 (0)