@@ -2532,7 +2532,7 @@ Plotly.relayout = function relayout(gd, astr, val) {
25322532 * @param {string id or DOM element } gd
25332533 * the id or DOM element of the graph container div
25342534 */
2535- Plotly . transition = function ( gd , data , layout , traceIndices , transitionConfig ) {
2535+ Plotly . transition = function ( gd , data , layout , traceIndices , transitionConfig , onTransitioned ) {
25362536 gd = getGraphDiv ( gd ) ;
25372537
25382538 var i , traceIdx ;
@@ -2700,6 +2700,8 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
27002700 gd . _transitioningWithDuration = false ;
27012701
27022702 gd . emit ( 'plotly_transitioned' , [ ] ) ;
2703+ onTransitioned && onTransitioned ( ) ;
2704+ onTransitioned = null ;
27032705 } ) ;
27042706 }
27052707
@@ -2753,22 +2755,133 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
27532755 * @param {object } transitionConfig
27542756 * configuration for transition
27552757 */
2756- Plotly . animate = function ( gd , frameName , transitionConfig ) {
2758+ Plotly . animate = function ( gd , groupNameOrFrameList , transitionConfig ) {
27572759 gd = getGraphDiv ( gd ) ;
2760+ var trans = gd . _transitionData ;
27582761
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+ }
27622794 }
27632795
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+ } ;
27652819
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 ( ) ;
27722885} ;
27732886
27742887/**
@@ -2780,7 +2893,7 @@ Plotly.animate = function(gd, frameName, transitionConfig) {
27802893 * - data: {array of objects} trace data
27812894 * - layout {object} layout definition
27822895 * - 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
27842897 */
27852898Plotly . addFrames = function ( gd , frameList , indices ) {
27862899 gd = getGraphDiv ( gd ) ;
@@ -2805,7 +2918,7 @@ Plotly.addFrames = function(gd, frameList, indices) {
28052918 var insertions = [ ] ;
28062919 for ( i = frameList . length - 1 ; i >= 0 ; i -- ) {
28072920 insertions . push ( {
2808- frame : frameList [ i ] ,
2921+ frame : Plots . supplyFrameDefaults ( frameList [ i ] ) ,
28092922 index : ( indices && indices [ i ] !== undefined && indices [ i ] !== null ) ? indices [ i ] : bigIndex + i
28102923 } ) ;
28112924 }
0 commit comments