@@ -2532,7 +2532,7 @@ Plotly.relayout = function relayout(gd, astr, val) {
2532
2532
* @param {string id or DOM element } gd
2533
2533
* the id or DOM element of the graph container div
2534
2534
*/
2535
- Plotly . transition = function ( gd , data , layout , traceIndices , transitionConfig ) {
2535
+ Plotly . transition = function ( gd , data , layout , traceIndices , transitionConfig , onTransitioned ) {
2536
2536
gd = getGraphDiv ( gd ) ;
2537
2537
2538
2538
var i , traceIdx ;
@@ -2700,6 +2700,8 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
2700
2700
gd . _transitioningWithDuration = false ;
2701
2701
2702
2702
gd . emit ( 'plotly_transitioned' , [ ] ) ;
2703
+ onTransitioned && onTransitioned ( ) ;
2704
+ onTransitioned = null ;
2703
2705
} ) ;
2704
2706
}
2705
2707
@@ -2753,22 +2755,133 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
2753
2755
* @param {object } transitionConfig
2754
2756
* configuration for transition
2755
2757
*/
2756
- Plotly . animate = function ( gd , frameName , transitionConfig ) {
2758
+ Plotly . animate = function ( gd , groupNameOrFrameList , transitionConfig ) {
2757
2759
gd = getGraphDiv ( gd ) ;
2760
+ var trans = gd . _transitionData ;
2758
2761
2759
- if ( ! gd . _transitionData . _frameHash [ frameName ] ) {
2760
- Lib . warn ( 'animateToFrame failure: keyframe does not exist' , frameName ) ;
2761
- return Promise . reject ( ) ;
2762
+ // This is the queue of frames that will be animated as soon as possible. They
2763
+ // are popped immediately upon the *start* of a transition:
2764
+ if ( ! trans . _frameQueue ) {
2765
+ trans . _frameQueue = [ ] ;
2766
+ }
2767
+
2768
+ // Since frames are popped immediately, an empty queue only means all frames have
2769
+ // *started* to transition, not that the animation is complete. To solve that,
2770
+ // track a separate counter that increments at the same time as frames are added
2771
+ // to the queue, but decrements only when the transition is complete.
2772
+ if ( trans . _frameWaitingCnt === undefined ) {
2773
+ trans . _frameWaitingCnt = 0 ;
2774
+ }
2775
+
2776
+ function queueFrames ( frameList ) {
2777
+ if ( frameList . length === 0 ) return ;
2778
+
2779
+ for ( var i = 0 ; i < frameList . length ; i ++ ) {
2780
+ var computedFrame = Plots . computeFrame ( gd , frameList [ i ] . name ) ;
2781
+
2782
+ trans . _frameWaitingCnt ++ ;
2783
+ trans . _frameQueue . push ( {
2784
+ frame : computedFrame ,
2785
+ name : frameList [ i ] . name ,
2786
+ transitionConfig : frameList [ i ] . transitionConfig || { } ,
2787
+ frameduration : 0 ,
2788
+ } ) ;
2789
+ }
2790
+
2791
+ if ( ! trans . _animationRaf ) {
2792
+ beginAnimation ( ) ;
2793
+ }
2762
2794
}
2763
2795
2764
- var computedFrame = Plots . computeFrame ( gd , frameName ) ;
2796
+ function completeAnimation ( ) {
2797
+ cancelAnimationFrame ( trans . _animationRaf ) ;
2798
+ trans . _animationRaf = null ;
2799
+ }
2800
+
2801
+ function beginAnimation ( ) {
2802
+ gd . emit ( 'plotly_animating' ) ;
2803
+
2804
+ // If no timer is running, then set last frame = long ago:
2805
+ trans . _lastframeat = 0 ;
2806
+ trans . _timetonext = 0 ;
2807
+
2808
+ var doFrame = function ( ) {
2809
+ // Check if we need to pop a frame:
2810
+ if ( Date . now ( ) - trans . _lastframeat > trans . _timetonext ) {
2811
+ var newFrame = trans . _frameQueue . shift ( ) ;
2812
+
2813
+ var onTransitioned = function ( ) {
2814
+ trans . _frameWaitingCnt -- ;
2815
+ if ( trans . _frameWaitingCnt === 0 ) {
2816
+ gd . emit ( 'plotly_animated' ) ;
2817
+ }
2818
+ } ;
2765
2819
2766
- return Plotly . transition ( gd ,
2767
- computedFrame . data ,
2768
- computedFrame . layout ,
2769
- computedFrame . traceIndices ,
2770
- transitionConfig
2771
- ) ;
2820
+ if ( newFrame ) {
2821
+ trans . _lastframeat = Date . now ( ) ;
2822
+ trans . _timetonext = newFrame . transitionConfig . frameduration === undefined ? 50 : newFrame . transitionConfig . frameduration ;
2823
+
2824
+
2825
+ Plotly . transition ( gd ,
2826
+ newFrame . frame . data ,
2827
+ newFrame . frame . layout ,
2828
+ newFrame . frame . traces ,
2829
+ newFrame . transitionConfig ,
2830
+ onTransitioned
2831
+ ) ;
2832
+ }
2833
+
2834
+ if ( trans . _frameQueue . length === 0 ) {
2835
+ completeAnimation ( ) ;
2836
+ return ;
2837
+ }
2838
+ }
2839
+
2840
+ trans . _animationRaf = requestAnimationFrame ( doFrame ) ;
2841
+ } ;
2842
+
2843
+ return doFrame ( ) ;
2844
+ }
2845
+
2846
+ var counter = 0 ;
2847
+ function setTransitionConfig ( frame ) {
2848
+ if ( Array . isArray ( transitionConfig ) ) {
2849
+ frame . transitionConfig = transitionConfig [ counter ] ;
2850
+ } else {
2851
+ frame . transitionConfig = transitionConfig ;
2852
+ }
2853
+ counter ++ ;
2854
+ return frame ;
2855
+ }
2856
+
2857
+ var i , frame ;
2858
+ var frameList = [ ] ;
2859
+ var allFrames = typeof groupNameOrFrameList === 'undefined' ;
2860
+ if ( allFrames || typeof groupNameOrFrameList === 'string' ) {
2861
+ for ( i = 0 ; i < trans . _frames . length ; i ++ ) {
2862
+ frame = trans . _frames [ i ] ;
2863
+
2864
+ if ( allFrames || frame . group === groupNameOrFrameList ) {
2865
+ frameList . push ( setTransitionConfig ( { name : frame . name } ) ) ;
2866
+ }
2867
+ }
2868
+ } else if ( Array . isArray ( groupNameOrFrameList ) ) {
2869
+ for ( i = 0 ; i < groupNameOrFrameList . length ; i ++ ) {
2870
+ frameList . push ( setTransitionConfig ( { name : groupNameOrFrameList [ i ] } ) ) ;
2871
+ }
2872
+ }
2873
+
2874
+ // Verify that all of these frames actually exist; return and reject if not:
2875
+ for ( i = 0 ; i < frameList . length ; i ++ ) {
2876
+ if ( ! trans . _frameHash [ frameList [ i ] . name ] ) {
2877
+ Lib . warn ( 'animate failure: frame not found: "' + frameList [ i ] . name + '"' ) ;
2878
+ return Promise . reject ( ) ;
2879
+ }
2880
+ }
2881
+
2882
+ queueFrames ( frameList ) ;
2883
+
2884
+ return Promise . resolve ( ) ;
2772
2885
} ;
2773
2886
2774
2887
/**
@@ -2780,7 +2893,7 @@ Plotly.animate = function(gd, frameName, transitionConfig) {
2780
2893
* - data: {array of objects} trace data
2781
2894
* - layout {object} layout definition
2782
2895
* - traces {array} trace indices
2783
- * - baseFrame {string} name of keyframe from which this keyframe gets defaults
2896
+ * - baseframe {string} name of keyframe from which this keyframe gets defaults
2784
2897
*/
2785
2898
Plotly . addFrames = function ( gd , frameList , indices ) {
2786
2899
gd = getGraphDiv ( gd ) ;
@@ -2805,7 +2918,7 @@ Plotly.addFrames = function(gd, frameList, indices) {
2805
2918
var insertions = [ ] ;
2806
2919
for ( i = frameList . length - 1 ; i >= 0 ; i -- ) {
2807
2920
insertions . push ( {
2808
- frame : frameList [ i ] ,
2921
+ frame : Plots . supplyFrameDefaults ( frameList [ i ] ) ,
2809
2922
index : ( indices && indices [ i ] !== undefined && indices [ i ] !== null ) ? indices [ i ] : bigIndex + i
2810
2923
} ) ;
2811
2924
}
0 commit comments