@@ -2738,9 +2738,11 @@ exports.react = function(gd, data, layout, config) {
27382738 var newFullData = gd . _fullData ;
27392739 var newFullLayout = gd . _fullLayout ;
27402740 var immutable = newFullLayout . datarevision === undefined ;
2741+ var transition = newFullLayout . transition ;
27412742
2742- var restyleFlags = diffData ( gd , oldFullData , newFullData , immutable ) ;
2743- var relayoutFlags = diffLayout ( gd , oldFullLayout , newFullLayout , immutable ) ;
2743+ var relayoutFlags = diffLayout ( gd , oldFullLayout , newFullLayout , immutable , transition ) ;
2744+ var newDataRevision = relayoutFlags . newDataRevision ;
2745+ var restyleFlags = diffData ( gd , oldFullData , newFullData , immutable , transition , newDataRevision ) ;
27442746
27452747 // TODO: how to translate this part of relayout to Plotly.react?
27462748 // // Setting width or height to null must reset the graph's width / height
@@ -2770,7 +2772,19 @@ exports.react = function(gd, data, layout, config) {
27702772 seq . push ( addFrames ) ;
27712773 }
27722774
2773- if ( restyleFlags . fullReplot || relayoutFlags . layoutReplot || configChanged ) {
2775+ // Transition pathway,
2776+ // only used when 'transition' is set by user and
2777+ // when at least one animatable attribute has changed,
2778+ // N.B. config changed aren't animatable
2779+ if ( newFullLayout . transition && ! configChanged && ( restyleFlags . anim || relayoutFlags . anim ) ) {
2780+ Plots . doCalcdata ( gd ) ;
2781+ subroutines . doAutoRangeAndConstraints ( gd ) ;
2782+
2783+ seq . push ( function ( ) {
2784+ return Plots . transitionFromReact ( gd , restyleFlags , relayoutFlags , oldFullLayout ) ;
2785+ } ) ;
2786+ }
2787+ else if ( restyleFlags . fullReplot || relayoutFlags . layoutReplot || configChanged ) {
27742788 gd . _fullLayout . _skipDefaults = true ;
27752789 seq . push ( exports . plot ) ;
27762790 }
@@ -2823,8 +2837,10 @@ exports.react = function(gd, data, layout, config) {
28232837
28242838} ;
28252839
2826- function diffData ( gd , oldFullData , newFullData , immutable ) {
2827- if ( oldFullData . length !== newFullData . length ) {
2840+ function diffData ( gd , oldFullData , newFullData , immutable , transition , newDataRevision ) {
2841+ var sameTraceLength = oldFullData . length === newFullData . length ;
2842+
2843+ if ( ! transition && ! sameTraceLength ) {
28282844 return {
28292845 fullReplot : true ,
28302846 calc : true
@@ -2833,6 +2849,9 @@ function diffData(gd, oldFullData, newFullData, immutable) {
28332849
28342850 var flags = editTypes . traceFlags ( ) ;
28352851 flags . arrays = { } ;
2852+ flags . nChanges = 0 ;
2853+ flags . nChangesAnim = 0 ;
2854+
28362855 var i , trace ;
28372856
28382857 function getTraceValObject ( parts ) {
@@ -2843,31 +2862,41 @@ function diffData(gd, oldFullData, newFullData, immutable) {
28432862 getValObject : getTraceValObject ,
28442863 flags : flags ,
28452864 immutable : immutable ,
2865+ transition : transition ,
2866+ newDataRevision : newDataRevision ,
28462867 gd : gd
28472868 } ;
28482869
2849-
28502870 var seenUIDs = { } ;
28512871
28522872 for ( i = 0 ; i < oldFullData . length ; i ++ ) {
2853- trace = newFullData [ i ] . _fullInput ;
2854- if ( Plots . hasMakesDataTransform ( trace ) ) trace = newFullData [ i ] ;
2855- if ( seenUIDs [ trace . uid ] ) continue ;
2856- seenUIDs [ trace . uid ] = 1 ;
2873+ if ( newFullData [ i ] ) {
2874+ trace = newFullData [ i ] . _fullInput ;
2875+ if ( Plots . hasMakesDataTransform ( trace ) ) trace = newFullData [ i ] ;
2876+ if ( seenUIDs [ trace . uid ] ) continue ;
2877+ seenUIDs [ trace . uid ] = 1 ;
28572878
2858- getDiffFlags ( oldFullData [ i ] . _fullInput , trace , [ ] , diffOpts ) ;
2879+ getDiffFlags ( oldFullData [ i ] . _fullInput , trace , [ ] , diffOpts ) ;
2880+ }
28592881 }
28602882
28612883 if ( flags . calc || flags . plot ) {
28622884 flags . fullReplot = true ;
28632885 }
28642886
2887+ if ( transition && flags . nChanges && flags . nChangesAnim ) {
2888+ flags . anim = ( flags . nChanges === flags . nChangesAnim ) && sameTraceLength ? 'all' : 'some' ;
2889+ }
2890+
28652891 return flags ;
28662892}
28672893
2868- function diffLayout ( gd , oldFullLayout , newFullLayout , immutable ) {
2894+ function diffLayout ( gd , oldFullLayout , newFullLayout , immutable , transition ) {
28692895 var flags = editTypes . layoutFlags ( ) ;
28702896 flags . arrays = { } ;
2897+ flags . rangesAltered = { } ;
2898+ flags . nChanges = 0 ;
2899+ flags . nChangesAnim = 0 ;
28712900
28722901 function getLayoutValObject ( parts ) {
28732902 return PlotSchema . getLayoutValObject ( newFullLayout , parts ) ;
@@ -2877,6 +2906,7 @@ function diffLayout(gd, oldFullLayout, newFullLayout, immutable) {
28772906 getValObject : getLayoutValObject ,
28782907 flags : flags ,
28792908 immutable : immutable ,
2909+ transition : transition ,
28802910 gd : gd
28812911 } ;
28822912
@@ -2886,11 +2916,15 @@ function diffLayout(gd, oldFullLayout, newFullLayout, immutable) {
28862916 flags . layoutReplot = true ;
28872917 }
28882918
2919+ if ( transition && flags . nChanges && flags . nChangesAnim ) {
2920+ flags . anim = flags . nChanges === flags . nChangesAnim ? 'all' : 'some' ;
2921+ }
2922+
28892923 return flags ;
28902924}
28912925
28922926function getDiffFlags ( oldContainer , newContainer , outerparts , opts ) {
2893- var valObject , key ;
2927+ var valObject , key , astr ;
28942928
28952929 var getValObject = opts . getValObject ;
28962930 var flags = opts . flags ;
@@ -2905,6 +2939,25 @@ function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
29052939 return ;
29062940 }
29072941 editTypes . update ( flags , valObject ) ;
2942+
2943+ if ( editType !== 'none' ) {
2944+ flags . nChanges ++ ;
2945+ }
2946+
2947+ // track animatable changes
2948+ if ( opts . transition && valObject . anim ) {
2949+ flags . nChangesAnim ++ ;
2950+ }
2951+
2952+ // track cartesian axes with altered ranges
2953+ if ( AX_RANGE_RE . test ( astr ) || AX_AUTORANGE_RE . test ( astr ) ) {
2954+ flags . rangesAltered [ outerparts [ 0 ] ] = 1 ;
2955+ }
2956+
2957+ // track datarevision changes
2958+ if ( key === 'datarevision' ) {
2959+ flags . newDataRevision = 1 ;
2960+ }
29082961 }
29092962
29102963 function valObjectCanBeDataArray ( valObject ) {
@@ -2913,10 +2966,12 @@ function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
29132966
29142967 for ( key in oldContainer ) {
29152968 // short-circuit based on previous calls or previous keys that already maximized the pathway
2916- if ( flags . calc ) return ;
2969+ if ( flags . calc && ! opts . transition ) return ;
29172970
29182971 var oldVal = oldContainer [ key ] ;
29192972 var newVal = newContainer [ key ] ;
2973+ var parts = outerparts . concat ( key ) ;
2974+ astr = parts . join ( '.' ) ;
29202975
29212976 if ( key . charAt ( 0 ) === '_' || typeof oldVal === 'function' || oldVal === newVal ) continue ;
29222977
@@ -2932,7 +2987,6 @@ function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
29322987 if ( key === 'range' && newContainer . autorange ) continue ;
29332988 if ( ( key === 'zmin' || key === 'zmax' ) && newContainer . type === 'contourcarpet' ) continue ;
29342989
2935- var parts = outerparts . concat ( key ) ;
29362990 valObject = getValObject ( parts ) ;
29372991
29382992 // in case type changed, we may not even *have* a valObject.
@@ -3003,6 +3057,11 @@ function getDiffFlags(oldContainer, newContainer, outerparts, opts) {
30033057 if ( immutable ) {
30043058 flags . calc = true ;
30053059 }
3060+
3061+ // look for animatable attributes when the data changed
3062+ if ( immutable || opts . newDataRevision ) {
3063+ changed ( ) ;
3064+ }
30063065 }
30073066 else if ( wasArray !== nowArray ) {
30083067 flags . calc = true ;
0 commit comments