@@ -45,10 +45,7 @@ function sankeyModel(layout, d, traceIndex) {
45
45
if ( circular ) {
46
46
sankey = d3SankeyCircular
47
47
. sankeyCircular ( )
48
- . circularLinkGap ( 0 )
49
- . nodeId ( function ( d ) {
50
- return d . pointNumber ;
51
- } ) ;
48
+ . circularLinkGap ( 0 ) ;
52
49
} else {
53
50
sankey = d3Sankey . sankey ( ) ;
54
51
}
@@ -58,6 +55,9 @@ function sankeyModel(layout, d, traceIndex) {
58
55
. size ( horizontal ? [ width , height ] : [ height , width ] )
59
56
. nodeWidth ( nodeThickness )
60
57
. nodePadding ( nodePad )
58
+ . nodeId ( function ( d ) {
59
+ return d . pointNumber ;
60
+ } )
61
61
. nodes ( nodes )
62
62
. links ( links ) ;
63
63
@@ -67,6 +67,36 @@ function sankeyModel(layout, d, traceIndex) {
67
67
Lib . warn ( 'node.pad was reduced to ' , sankey . nodePadding ( ) , ' to fit within the figure.' ) ;
68
68
}
69
69
70
+ // Create transient nodes for animations
71
+ for ( var nodePointNumber in calcData . _groupLookup ) {
72
+ var groupIndex = parseInt ( calcData . _groupLookup [ nodePointNumber ] ) ;
73
+
74
+ // Find node representing groupIndex
75
+ var groupingNode ;
76
+ for ( var i = 0 ; i < graph . nodes . length ; i ++ ) {
77
+ if ( graph . nodes [ i ] . pointNumber === groupIndex ) {
78
+ groupingNode = graph . nodes [ i ] ;
79
+ break ;
80
+ }
81
+ }
82
+ // If groupinNode is undefined, no links are targeting this group
83
+ if ( ! groupingNode ) continue ;
84
+
85
+ var child = {
86
+ pointNumber : parseInt ( nodePointNumber ) ,
87
+ x0 : groupingNode . x0 ,
88
+ x1 : groupingNode . x1 ,
89
+ y0 : groupingNode . y0 ,
90
+ y1 : groupingNode . y1 ,
91
+ partOfGroup : true ,
92
+ sourceLinks : [ ] ,
93
+ targetLinks : [ ]
94
+ } ;
95
+
96
+ graph . nodes . unshift ( child ) ;
97
+ groupingNode . childrenNodes . unshift ( child ) ;
98
+ }
99
+
70
100
function computeLinkConcentrations ( ) {
71
101
var i , j , k ;
72
102
for ( i = 0 ; i < graph . nodes . length ; i ++ ) {
@@ -137,7 +167,7 @@ function sankeyModel(layout, d, traceIndex) {
137
167
circular : circular ,
138
168
key : traceIndex ,
139
169
trace : trace ,
140
- guid : Math . floor ( 1e12 * ( 1 + Math . random ( ) ) ) ,
170
+ guid : Lib . randstr ( ) ,
141
171
horizontal : horizontal ,
142
172
width : width ,
143
173
height : height ,
@@ -184,6 +214,7 @@ function linkModel(d, l, i) {
184
214
link : l ,
185
215
tinyColorHue : Color . tinyRGB ( tc ) ,
186
216
tinyColorAlpha : tc . getAlpha ( ) ,
217
+ linkPath : linkPath ,
187
218
linkLineColor : d . linkLineColor ,
188
219
linkLineWidth : d . linkLineWidth ,
189
220
valueFormat : d . valueFormat ,
@@ -343,7 +374,7 @@ function linkPath() {
343
374
return path ;
344
375
}
345
376
346
- function nodeModel ( d , n , i ) {
377
+ function nodeModel ( d , n ) {
347
378
var tc = tinycolor ( n . color ) ;
348
379
var zoneThicknessPad = c . nodePadAcross ;
349
380
var zoneLengthPad = d . nodePad / 2 ;
@@ -352,8 +383,11 @@ function nodeModel(d, n, i) {
352
383
var visibleThickness = n . dx ;
353
384
var visibleLength = Math . max ( 0.5 , n . dy ) ;
354
385
355
- var basicKey = n . label ;
356
- var key = basicKey + '__' + i ;
386
+ var key = 'node_' + n . pointNumber ;
387
+ // If it's a group, it's mutable and should be unique
388
+ if ( n . group ) {
389
+ key = Lib . randstr ( ) ;
390
+ }
357
391
358
392
// for event data
359
393
n . trace = d . trace ;
@@ -362,6 +396,8 @@ function nodeModel(d, n, i) {
362
396
return {
363
397
index : n . pointNumber ,
364
398
key : key ,
399
+ partOfGroup : n . partOfGroup || false ,
400
+ group : n . group ,
365
401
traceId : d . key ,
366
402
node : n ,
367
403
nodePad : d . nodePad ,
@@ -445,19 +481,19 @@ function attachPointerEvents(selection, sankey, eventSet) {
445
481
selection
446
482
. on ( '.basic' , null ) // remove any preexisting handlers
447
483
. on ( 'mouseover.basic' , function ( d ) {
448
- if ( ! d . interactionState . dragInProgress ) {
484
+ if ( ! d . interactionState . dragInProgress && ! d . partOfGroup ) {
449
485
eventSet . hover ( this , d , sankey ) ;
450
486
d . interactionState . hovered = [ this , d ] ;
451
487
}
452
488
} )
453
489
. on ( 'mousemove.basic' , function ( d ) {
454
- if ( ! d . interactionState . dragInProgress ) {
490
+ if ( ! d . interactionState . dragInProgress && ! d . partOfGroup ) {
455
491
eventSet . follow ( this , d ) ;
456
492
d . interactionState . hovered = [ this , d ] ;
457
493
}
458
494
} )
459
495
. on ( 'mouseout.basic' , function ( d ) {
460
- if ( ! d . interactionState . dragInProgress ) {
496
+ if ( ! d . interactionState . dragInProgress && ! d . partOfGroup ) {
461
497
eventSet . unhover ( this , d , sankey ) ;
462
498
d . interactionState . hovered = false ;
463
499
}
@@ -467,7 +503,7 @@ function attachPointerEvents(selection, sankey, eventSet) {
467
503
eventSet . unhover ( this , d , sankey ) ;
468
504
d . interactionState . hovered = false ;
469
505
}
470
- if ( ! d . interactionState . dragInProgress ) {
506
+ if ( ! d . interactionState . dragInProgress && ! d . partOfGroup ) {
471
507
eventSet . select ( this , d , sankey ) ;
472
508
}
473
509
} ) ;
@@ -530,6 +566,10 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) {
530
566
531
567
. on ( 'dragend' , function ( d ) {
532
568
d . interactionState . dragInProgress = false ;
569
+ for ( var i = 0 ; i < d . node . childrenNodes . length ; i ++ ) {
570
+ d . node . childrenNodes [ i ] . x = d . node . x ;
571
+ d . node . childrenNodes [ i ] . y = d . node . y ;
572
+ }
533
573
} ) ;
534
574
535
575
sankeyNode
@@ -540,7 +580,10 @@ function attachDragHandler(sankeyNode, sankeyLink, callbacks) {
540
580
function attachForce ( sankeyNode , forceKey , d ) {
541
581
// Attach force to nodes in the same column (same x coordinate)
542
582
switchToForceFormat ( d . graph . nodes ) ;
543
- var nodes = d . graph . nodes . filter ( function ( n ) { return n . originalX === d . node . originalX ; } ) ;
583
+ var nodes = d . graph . nodes
584
+ . filter ( function ( n ) { return n . originalX === d . node . originalX ; } )
585
+ // Filter out children
586
+ . filter ( function ( n ) { return ! n . partOfGroup ; } ) ;
544
587
d . forceLayouts [ forceKey ] = d3Force . forceSimulation ( nodes )
545
588
. alphaDecay ( 0 )
546
589
. force ( 'collide' , d3Force . forceCollide ( )
@@ -639,6 +682,11 @@ function switchToSankeyFormat(nodes) {
639
682
640
683
// scene graph
641
684
module . exports = function ( gd , svg , calcData , layout , callbacks ) {
685
+ // To prevent animation on first render
686
+ var firstRender = false ;
687
+ Lib . ensureSingle ( gd . _fullLayout . _infolayer , 'g' , 'first-render' , function ( ) {
688
+ firstRender = true ;
689
+ } ) ;
642
690
643
691
var styledData = calcData
644
692
. filter ( function ( d ) { return unwrap ( d ) . trace . visible ; } )
@@ -683,7 +731,6 @@ module.exports = function(gd, svg, calcData, layout, callbacks) {
683
731
sankeyLink
684
732
. enter ( ) . append ( 'path' )
685
733
. classed ( c . cn . sankeyLink , true )
686
- . attr ( 'd' , linkPath ( ) )
687
734
. call ( attachPointerEvents , sankey , callbacks . linkEvents ) ;
688
735
689
736
sankeyLink
@@ -701,13 +748,17 @@ module.exports = function(gd, svg, calcData, layout, callbacks) {
701
748
} )
702
749
. style ( 'stroke-width' , function ( d ) {
703
750
return salientEnough ( d ) ? d . linkLineWidth : 1 ;
704
- } ) ;
751
+ } )
752
+ . attr ( 'd' , linkPath ( ) ) ;
705
753
706
- sankeyLink . transition ( )
707
- . ease ( c . ease ) . duration ( c . duration )
708
- . attr ( 'd' , linkPath ( ) ) ;
754
+ sankeyLink
755
+ . style ( 'opacity' , function ( ) { return ( gd . _context . staticPlot || firstRender ) ? 1 : 0 ; } )
756
+ . transition ( )
757
+ . ease ( c . ease ) . duration ( c . duration )
758
+ . style ( 'opacity' , 1 ) ;
709
759
710
- sankeyLink . exit ( ) . transition ( )
760
+ sankeyLink . exit ( )
761
+ . transition ( )
711
762
. ease ( c . ease ) . duration ( c . duration )
712
763
. style ( 'opacity' , 0 )
713
764
. remove ( ) ;
@@ -733,24 +784,26 @@ module.exports = function(gd, svg, calcData, layout, callbacks) {
733
784
var nodes = d . graph . nodes ;
734
785
persistOriginalPlace ( nodes ) ;
735
786
return nodes
736
- . filter ( function ( n ) { return n . value ; } )
737
- . map ( nodeModel . bind ( null , d ) ) ;
787
+ . map ( nodeModel . bind ( null , d ) ) ;
738
788
} , keyFun ) ;
739
789
740
790
sankeyNode . enter ( )
741
791
. append ( 'g' )
742
792
. classed ( c . cn . sankeyNode , true )
743
793
. call ( updateNodePositions )
744
- . call ( attachPointerEvents , sankey , callbacks . nodeEvents ) ;
794
+ . style ( 'opacity' , function ( n ) { return ( ( gd . _context . staticPlot || firstRender ) && ! n . partOfGroup ) ? 1 : 0 ; } ) ;
745
795
746
796
sankeyNode
797
+ . call ( attachPointerEvents , sankey , callbacks . nodeEvents )
747
798
. call ( attachDragHandler , sankeyLink , callbacks ) ; // has to be here as it binds sankeyLink
748
799
749
800
sankeyNode . transition ( )
750
801
. ease ( c . ease ) . duration ( c . duration )
751
- . call ( updateNodePositions ) ;
802
+ . call ( updateNodePositions )
803
+ . style ( 'opacity' , function ( n ) { return n . partOfGroup ? 0 : 1 ; } ) ;
752
804
753
- sankeyNode . exit ( ) . transition ( )
805
+ sankeyNode . exit ( )
806
+ . transition ( )
754
807
. ease ( c . ease ) . duration ( c . duration )
755
808
. style ( 'opacity' , 0 )
756
809
. remove ( ) ;
0 commit comments