Skip to content

Commit a07d471

Browse files
authored
Merge pull request #20321 from apache/feat-sankey-roam
(feat) Sankey roaming
2 parents bd1fbe8 + be096e7 commit a07d471

File tree

6 files changed

+281
-44
lines changed

6 files changed

+281
-44
lines changed

src/chart/sankey/SankeySeries.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,14 @@ import {
3434
GraphEdgeItemObject,
3535
OptionDataValueNumeric,
3636
DefaultEmphasisFocus,
37-
CallbackDataParams
37+
CallbackDataParams,
38+
RoamOptionMixin
3839
} from '../../util/types';
3940
import GlobalModel from '../../model/Global';
4041
import SeriesData from '../../data/SeriesData';
4142
import { LayoutRect } from '../../util/layout';
4243
import { createTooltipMarkup } from '../../component/tooltip/tooltipMarkup';
44+
import type View from '../../coord/View';
4345

4446

4547
type FocusNodeAdjacency = boolean | 'inEdges' | 'outEdges' | 'allEdges';
@@ -95,7 +97,8 @@ export interface SankeyLevelOption extends SankeyNodeStateOption, SankeyEdgeStat
9597
export interface SankeySeriesOption
9698
extends SeriesOption<SankeyBothStateOption<CallbackDataParams>, ExtraStateOption>,
9799
SankeyBothStateOption<CallbackDataParams>,
98-
BoxLayoutOptionMixin {
100+
BoxLayoutOptionMixin,
101+
RoamOptionMixin {
99102
type?: 'sankey'
100103

101104
/**
@@ -148,6 +151,8 @@ class SankeySeriesModel extends SeriesModel<SankeySeriesOption> {
148151
static readonly type = 'series.sankey';
149152
readonly type = SankeySeriesModel.type;
150153

154+
coordinateSystem: View;
155+
151156
levelModels: Model<SankeyLevelOption>[];
152157

153158
layoutInfo: LayoutRect;
@@ -213,6 +218,14 @@ class SankeySeriesModel extends SeriesModel<SankeySeriesOption> {
213218
dataItem.localY = localPosition[1];
214219
}
215220

221+
setCenter(center: number[]) {
222+
this.option.center = center;
223+
}
224+
225+
setZoom(zoom: number) {
226+
this.option.zoom = zoom;
227+
}
228+
216229
/**
217230
* Return the graphic data structure
218231
*
@@ -297,6 +310,11 @@ class SankeySeriesModel extends SeriesModel<SankeySeriesOption> {
297310

298311
layoutIterations: 32,
299312

313+
// true | false | 'move' | 'scale', see module:component/helper/RoamController.
314+
roam: false,
315+
center: null,
316+
zoom: 1,
317+
300318
label: {
301319
show: true,
302320
position: 'right',

src/chart/sankey/SankeyView.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ import { setLabelStyle, getLabelStatesModels } from '../../label/labelStyle';
3131
import { getECData } from '../../util/innerStore';
3232
import { isString, retrieve3 } from 'zrender/src/core/util';
3333
import type { GraphEdge } from '../../data/Graph';
34+
import RoamController from '../../component/helper/RoamController';
35+
import * as roamHelper from '../../component/helper/roamHelper';
36+
import View from '../../coord/View';
3437

3538
class SankeyPathShape {
3639
x1 = 0;
@@ -106,15 +109,28 @@ class SankeyView extends ChartView {
106109
readonly type = SankeyView.type;
107110

108111
private _model: SankeySeriesModel;
109-
112+
private _mainGroup = new graphic.Group();
110113
private _focusAdjacencyDisabled = false;
111114

112115
private _data: SeriesData;
113116

117+
private _controller: RoamController;
118+
private _controllerHost: roamHelper.RoamControllerHost;
119+
120+
init(ecModel: GlobalModel, api: ExtensionAPI): void {
121+
this._controller = new RoamController(api.getZr());
122+
123+
this._controllerHost = {
124+
target: this.group
125+
} as roamHelper.RoamControllerHost;
126+
127+
this.group.add(this._mainGroup);
128+
}
129+
114130
render(seriesModel: SankeySeriesModel, ecModel: GlobalModel, api: ExtensionAPI) {
115131
const sankeyView = this;
116132
const graph = seriesModel.getGraph();
117-
const group = this.group;
133+
const group = this._mainGroup;
118134
const layoutInfo = seriesModel.layoutInfo;
119135
// view width
120136
const width = layoutInfo.width;
@@ -128,8 +144,8 @@ class SankeyView extends ChartView {
128144

129145
group.removeAll();
130146

131-
group.x = layoutInfo.x;
132-
group.y = layoutInfo.y;
147+
this._updateViewCoordSys(seriesModel, api);
148+
roamHelper.updateController(seriesModel, api, group, this._controller, this._controllerHost);
133149

134150
// generate a bezire Curve for each edge
135151
graph.eachEdge(function (edge) {
@@ -346,6 +362,25 @@ class SankeyView extends ChartView {
346362
}
347363

348364
dispose() {
365+
this._controller && this._controller.dispose();
366+
this._controllerHost = null;
367+
}
368+
369+
private _updateViewCoordSys(seriesModel: SankeySeriesModel, api: ExtensionAPI) {
370+
const layoutInfo = seriesModel.layoutInfo;
371+
const width = layoutInfo.width;
372+
const height = layoutInfo.height;
373+
374+
const viewCoordSys = seriesModel.coordinateSystem = new View();
375+
viewCoordSys.zoomLimit = seriesModel.get('scaleLimit');
376+
377+
viewCoordSys.setBoundingRect(0, 0, width, height);
378+
379+
viewCoordSys.setCenter(seriesModel.get('center'), api);
380+
viewCoordSys.setZoom(seriesModel.get('zoom'));
381+
382+
this._mainGroup.x = layoutInfo.x;
383+
this._mainGroup.y = layoutInfo.y;
349384
}
350385
}
351386

src/chart/sankey/install.ts

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import sankeyLayout from './sankeyLayout';
2525
import sankeyVisual from './sankeyVisual';
2626
import { Payload } from '../../util/types';
2727
import GlobalModel from '../../model/Global';
28+
import { updateCenterAndZoom, RoamPayload } from '../../action/roamHelper';
29+
import type ExtensionAPI from '../../core/ExtensionAPI';
2830

2931
interface SankeyDragNodePayload extends Payload {
3032
localX: number
@@ -53,4 +55,21 @@ export function install(registers: EChartsExtensionInstallRegisters) {
5355
});
5456
});
5557

56-
}
58+
registers.registerAction({
59+
type: 'sankeyRoam',
60+
event: 'sankeyRoam',
61+
update: 'none'
62+
}, function (payload: RoamPayload, ecModel: GlobalModel, api: ExtensionAPI) {
63+
ecModel.eachComponent({
64+
mainType: 'series', subType: 'sankey', query: payload
65+
}, function (seriesModel: SankeySeriesModel) {
66+
const coordSys = seriesModel.coordinateSystem;
67+
const res = updateCenterAndZoom(coordSys, payload, undefined, api);
68+
69+
seriesModel.setCenter
70+
&& seriesModel.setCenter(res.center);
71+
seriesModel.setZoom
72+
&& seriesModel.setZoom(res.zoom);
73+
});
74+
});
75+
}

src/chart/tree/TreeView.ts

Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import * as bbox from 'zrender/src/core/bbox';
2626
import View from '../../coord/View';
2727
import * as roamHelper from '../../component/helper/roamHelper';
2828
import RoamController from '../../component/helper/RoamController';
29-
import {onIrrelevantElement} from '../../component/helper/cursorHelper';
3029
import {parsePercent} from '../../util/number';
3130
import ChartView from '../../view/Chart';
3231
import TreeSeriesModel, { TreeSeriesOption, TreeSeriesNodeItemOption } from './TreeSeries';
@@ -277,44 +276,17 @@ class TreeView extends ChartView {
277276
ecModel: GlobalModel,
278277
api: ExtensionAPI
279278
) {
280-
const controller = this._controller;
281-
const controllerHost = this._controllerHost;
282-
const group = this.group;
283-
controller.setPointerChecker(function (e, x, y) {
284-
const rect = group.getBoundingRect();
285-
rect.applyTransform(group.transform);
286-
return rect.contain(x, y)
287-
&& !onIrrelevantElement(e, api, seriesModel);
288-
});
289-
290-
controller.enable(seriesModel.get('roam'));
291-
controllerHost.zoomLimit = seriesModel.get('scaleLimit');
292-
controllerHost.zoom = seriesModel.coordinateSystem.getZoom();
293-
294-
controller
295-
.off('pan')
296-
.off('zoom')
297-
.on('pan', (e) => {
298-
roamHelper.updateViewOnPan(controllerHost, e.dx, e.dy);
299-
api.dispatchAction({
300-
seriesId: seriesModel.id,
301-
type: 'treeRoam',
302-
dx: e.dx,
303-
dy: e.dy
304-
});
305-
})
279+
roamHelper.updateController(
280+
seriesModel,
281+
api,
282+
this.group,
283+
this._controller,
284+
this._controllerHost
285+
);
286+
287+
this._controller
306288
.on('zoom', (e) => {
307-
roamHelper.updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);
308-
api.dispatchAction({
309-
seriesId: seriesModel.id,
310-
type: 'treeRoam',
311-
zoom: e.scale,
312-
originX: e.originX,
313-
originY: e.originY
314-
});
315289
this._updateNodeAndLinkScale(seriesModel);
316-
// Only update label layout on zoom
317-
api.updateLabelLayout();
318290
});
319291
}
320292

src/component/helper/roamHelper.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
*/
1919

2020
import Element from 'zrender/src/Element';
21+
import { SeriesModel } from '../../echarts.all';
22+
import ExtensionAPI from '../../core/ExtensionAPI';
23+
import Group from 'zrender/src/graphic/Group';
24+
import RoamController from './RoamController';
25+
import type { SeriesOption } from '../../export/option';
26+
import type View from '../../coord/View';
27+
import type { RoamOptionMixin } from '../../util/types';
28+
import { onIrrelevantElement } from './cursorHelper';
2129

2230
export interface RoamControllerHost {
2331
target: Element;
@@ -62,3 +70,49 @@ export function updateViewOnZoom(controllerHost: RoamControllerHost, zoomDelta:
6270

6371
target.dirty();
6472
}
73+
74+
export function updateController(
75+
seriesModel: SeriesModel<SeriesOption & RoamOptionMixin>,
76+
api: ExtensionAPI,
77+
group: Group,
78+
controller: RoamController,
79+
controllerHost: RoamControllerHost,
80+
) {
81+
controller.setPointerChecker(function (e, x, y) {
82+
const rect = group.getBoundingRect();
83+
rect.applyTransform(group.transform);
84+
return rect.contain(x, y)
85+
&& !onIrrelevantElement(e, api, seriesModel);
86+
});
87+
88+
controller.enable(seriesModel.get('roam'));
89+
controllerHost.zoomLimit = seriesModel.get('scaleLimit');
90+
const coordinate = seriesModel.coordinateSystem;
91+
controllerHost.zoom = coordinate ? (coordinate as View).getZoom() : 1;
92+
const type = seriesModel.type + 'Roam';
93+
94+
controller
95+
.off('pan')
96+
.off('zoom')
97+
.on('pan', (e) => {
98+
updateViewOnPan(controllerHost, e.dx, e.dy);
99+
api.dispatchAction({
100+
seriesId: seriesModel.id,
101+
type,
102+
dx: e.dx,
103+
dy: e.dy
104+
});
105+
})
106+
.on('zoom', (e) => {
107+
updateViewOnZoom(controllerHost, e.scale, e.originX, e.originY);
108+
api.dispatchAction({
109+
seriesId: seriesModel.id,
110+
type,
111+
zoom: e.scale,
112+
originX: e.originX,
113+
originY: e.originY
114+
});
115+
// Only update label layout on zoom
116+
api.updateLabelLayout();
117+
});
118+
}

0 commit comments

Comments
 (0)