From 624f520edd5e0e01c4387ee65b95d2b6f781e662 Mon Sep 17 00:00:00 2001 From: konewka17 <56441194+konewka17@users.noreply.github.com> Date: Tue, 31 Mar 2026 19:52:42 +0200 Subject: [PATCH 1/2] feat(sankey): support disabling node sorting. close #21561 --- src/chart/sankey/SankeySeries.ts | 6 ++++++ src/chart/sankey/sankeyLayout.ts | 29 +++++++++++++++++----------- test/sankey.html | 33 ++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 11 deletions(-) diff --git a/src/chart/sankey/SankeySeries.ts b/src/chart/sankey/SankeySeries.ts index ebd85227ab..a3ca46f19e 100644 --- a/src/chart/sankey/SankeySeries.ts +++ b/src/chart/sankey/SankeySeries.ts @@ -136,6 +136,11 @@ export interface SankeySeriesOption * The number of iterations to change the position of the node */ layoutIterations?: number + /** + * Whether to sort nodes by current coordinate when resolving collisions. + * Set to false to preserve the original node order in each depth column. + */ + sortNodes?: boolean nodeAlign?: 'justify' | 'left' | 'right' // TODO justify should be auto @@ -318,6 +323,7 @@ class SankeySeriesModel extends SeriesModel { draggable: true, layoutIterations: 32, + sortNodes: true, // true | false | 'move' | 'scale', see module:component/helper/RoamController. roam: false, diff --git a/src/chart/sankey/sankeyLayout.ts b/src/chart/sankey/sankeyLayout.ts index 8e9c8d22bb..8e0ce9e619 100644 --- a/src/chart/sankey/sankeyLayout.ts +++ b/src/chart/sankey/sankeyLayout.ts @@ -57,8 +57,9 @@ export default function sankeyLayout(ecModel: GlobalModel, api: ExtensionAPI) { const orient = seriesModel.get('orient'); const nodeAlign = seriesModel.get('nodeAlign'); + const sortNodes = seriesModel.get('sortNodes'); - layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign); + layoutSankey(nodes, edges, nodeWidth, nodeGap, width, height, iterations, orient, nodeAlign, sortNodes); }); } @@ -71,10 +72,11 @@ function layoutSankey( height: number, iterations: number, orient: LayoutOrient, - nodeAlign: SankeySeriesOption['nodeAlign'] + nodeAlign: SankeySeriesOption['nodeAlign'], + sortNodes: boolean ) { computeNodeBreadths(nodes, edges, nodeWidth, width, height, orient, nodeAlign); - computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient); + computeNodeDepths(nodes, edges, height, width, nodeGap, iterations, orient, sortNodes); computeEdgeDepths(nodes, orient); } @@ -257,6 +259,7 @@ function scaleNodeBreadths(nodes: GraphNode[], kx: number, orient: LayoutOrient) * @param nodeGap the vertical distance between two nodes * in the same column. * @param iterations the number of iterations for the algorithm + * @param sortNodes whether to sort the nodes by y-position */ function computeNodeDepths( nodes: GraphNode[], @@ -265,21 +268,22 @@ function computeNodeDepths( width: number, nodeGap: number, iterations: number, - orient: LayoutOrient + orient: LayoutOrient, + sortNodes: boolean ) { const nodesByBreadth = prepareNodesByBreadth(nodes, orient); initializeNodeDepth(nodesByBreadth, edges, height, width, nodeGap, orient); - resolveCollisions(nodesByBreadth, nodeGap, height, width, orient); + resolveCollisions(nodesByBreadth, nodeGap, height, width, orient, sortNodes); for (let alpha = 1; iterations > 0; iterations--) { // 0.99 is a experience parameter, ensure that each iterations of // changes as small as possible. alpha *= 0.99; relaxRightToLeft(nodesByBreadth, alpha, orient); - resolveCollisions(nodesByBreadth, nodeGap, height, width, orient); + resolveCollisions(nodesByBreadth, nodeGap, height, width, orient, sortNodes); relaxLeftToRight(nodesByBreadth, alpha, orient); - resolveCollisions(nodesByBreadth, nodeGap, height, width, orient); + resolveCollisions(nodesByBreadth, nodeGap, height, width, orient, sortNodes); } } @@ -355,13 +359,16 @@ function resolveCollisions( nodeGap: number, height: number, width: number, - orient: LayoutOrient + orient: LayoutOrient, + sortNodes: boolean ) { const keyAttr = orient === 'vertical' ? 'x' : 'y'; zrUtil.each(nodesByBreadth, function (nodes) { - nodes.sort(function (a, b) { - return a.getLayout()[keyAttr] - b.getLayout()[keyAttr]; - }); + if (sortNodes !== false) { + nodes.sort(function (a, b) { + return a.getLayout()[keyAttr] - b.getLayout()[keyAttr]; + }); + } let nodeX; let node; let dy; diff --git a/test/sankey.html b/test/sankey.html index 7b6205d108..877187a11a 100644 --- a/test/sankey.html +++ b/test/sankey.html @@ -39,6 +39,7 @@
+
@@ -178,6 +179,38 @@ }); + + + + + + + + + + + + + +
+
+
+ + + + + + + diff --git a/test/sankey.html b/test/sankey.html index 877187a11a..7b6205d108 100644 --- a/test/sankey.html +++ b/test/sankey.html @@ -39,7 +39,6 @@
-
@@ -179,38 +178,6 @@ }); - -