@@ -14,7 +14,9 @@ describe('Plots.supplyAnimationDefaults', function() {
1414
1515 it ( 'supplies transition defaults' , function ( ) {
1616 expect ( Plots . supplyAnimationDefaults ( { } ) ) . toEqual ( {
17+ fromcurrent : false ,
1718 mode : 'afterall' ,
19+ direction : 'forward' ,
1820 transition : {
1921 duration : 500 ,
2022 easing : 'cubic-in-out'
@@ -29,6 +31,8 @@ describe('Plots.supplyAnimationDefaults', function() {
2931 it ( 'uses provided values' , function ( ) {
3032 expect ( Plots . supplyAnimationDefaults ( {
3133 mode : 'next' ,
34+ fromcurrent : true ,
35+ direction : 'reverse' ,
3236 transition : {
3337 duration : 600 ,
3438 easing : 'elastic-in-out'
@@ -39,6 +43,8 @@ describe('Plots.supplyAnimationDefaults', function() {
3943 }
4044 } ) ) . toEqual ( {
4145 mode : 'next' ,
46+ fromcurrent : true ,
47+ direction : 'reverse' ,
4248 transition : {
4349 duration : 600 ,
4450 easing : 'elastic-in-out'
@@ -63,7 +69,12 @@ describe('Test animate API', function() {
6369 function verifyFrameTransitionOrder ( gd , expectedFrames ) {
6470 var calls = Plots . transition . calls ;
6571
66- expect ( calls . count ( ) ) . toEqual ( expectedFrames . length ) ;
72+ var c1 = calls . count ( ) ;
73+ var c2 = expectedFrames . length ;
74+ expect ( c1 ) . toEqual ( c2 ) ;
75+
76+ // Prevent lots of ugly logging when it's already failed:
77+ if ( c1 !== c2 ) return ;
6778
6879 for ( var i = 0 ; i < calls . count ( ) ; i ++ ) {
6980 expect ( calls . argsFor ( i ) [ 1 ] ) . toEqual (
@@ -315,6 +326,104 @@ describe('Test animate API', function() {
315326 } ) ;
316327 }
317328
329+ describe ( 'Animation direction' , function ( ) {
330+ var animOpts ;
331+
332+ beforeEach ( function ( ) {
333+ animOpts = {
334+ frame : { duration : 0 } ,
335+ transition : { duration : 0 }
336+ } ;
337+ } ) ;
338+
339+ it ( 'animates frames by name in reverse' , function ( done ) {
340+ animOpts . direction = 'reverse' ;
341+
342+ Plotly . animate ( gd , [ 'frame0' , 'frame2' , 'frame1' , 'frame3' ] , animOpts ) . then ( function ( ) {
343+ verifyFrameTransitionOrder ( gd , [ 'frame3' , 'frame1' , 'frame2' , 'frame0' ] ) ;
344+ verifyQueueEmpty ( gd ) ;
345+ } ) . catch ( fail ) . then ( done ) ;
346+ } ) ;
347+
348+ it ( 'animates a group in reverse' , function ( done ) {
349+ animOpts . direction = 'reverse' ;
350+ Plotly . animate ( gd , 'even-frames' , animOpts ) . then ( function ( ) {
351+ verifyFrameTransitionOrder ( gd , [ 'frame2' , 'frame0' ] ) ;
352+ verifyQueueEmpty ( gd ) ;
353+ } ) . catch ( fail ) . then ( done ) ;
354+ } ) ;
355+ } ) ;
356+
357+ describe ( 'Animation fromcurrent' , function ( ) {
358+ var animOpts ;
359+
360+ beforeEach ( function ( ) {
361+ animOpts = {
362+ frame : { duration : 0 } ,
363+ transition : { duration : 0 } ,
364+ fromcurrent : true
365+ } ;
366+ } ) ;
367+
368+ it ( 'animates starting at the current frame' , function ( done ) {
369+ Plotly . animate ( gd , [ 'frame1' ] , animOpts ) . then ( function ( ) {
370+ verifyFrameTransitionOrder ( gd , [ 'frame1' ] ) ;
371+ verifyQueueEmpty ( gd ) ;
372+
373+ return Plotly . animate ( gd , null , animOpts ) ;
374+ } ) . then ( function ( ) {
375+ verifyFrameTransitionOrder ( gd , [ 'frame1' , 'frame2' , 'frame3' ] ) ;
376+ verifyQueueEmpty ( gd ) ;
377+ } ) . catch ( fail ) . then ( done ) ;
378+ } ) ;
379+
380+ it ( 'plays from the start when current frame = last frame' , function ( done ) {
381+ Plotly . animate ( gd , null , animOpts ) . then ( function ( ) {
382+ verifyFrameTransitionOrder ( gd , [ 'base' , 'frame0' , 'frame1' , 'frame2' , 'frame3' ] ) ;
383+ verifyQueueEmpty ( gd ) ;
384+
385+ return Plotly . animate ( gd , null , animOpts ) ;
386+ } ) . then ( function ( ) {
387+ verifyFrameTransitionOrder ( gd , [
388+ 'base' , 'frame0' , 'frame1' , 'frame2' , 'frame3' ,
389+ 'base' , 'frame0' , 'frame1' , 'frame2' , 'frame3'
390+ ] ) ;
391+
392+ verifyQueueEmpty ( gd ) ;
393+ } ) . catch ( fail ) . then ( done ) ;
394+ } ) ;
395+
396+ it ( 'animates in reverse starting at the current frame' , function ( done ) {
397+ animOpts . direction = 'reverse' ;
398+
399+ Plotly . animate ( gd , [ 'frame1' ] , animOpts ) . then ( function ( ) {
400+ verifyFrameTransitionOrder ( gd , [ 'frame1' ] ) ;
401+ verifyQueueEmpty ( gd ) ;
402+ return Plotly . animate ( gd , null , animOpts ) ;
403+ } ) . then ( function ( ) {
404+ verifyFrameTransitionOrder ( gd , [ 'frame1' , 'frame0' , 'base' ] ) ;
405+ verifyQueueEmpty ( gd ) ;
406+ } ) . catch ( fail ) . then ( done ) ;
407+ } ) ;
408+
409+ it ( 'plays in reverse from the end when current frame = first frame' , function ( done ) {
410+ animOpts . direction = 'reverse' ;
411+
412+ Plotly . animate ( gd , [ 'base' ] , animOpts ) . then ( function ( ) {
413+ verifyFrameTransitionOrder ( gd , [ 'base' ] ) ;
414+ verifyQueueEmpty ( gd ) ;
415+
416+ return Plotly . animate ( gd , null , animOpts ) ;
417+ } ) . then ( function ( ) {
418+ verifyFrameTransitionOrder ( gd , [
419+ 'base' , 'frame3' , 'frame2' , 'frame1' , 'frame0' , 'base'
420+ ] ) ;
421+
422+ verifyQueueEmpty ( gd ) ;
423+ } ) . catch ( fail ) . then ( done ) ;
424+ } ) ;
425+ } ) ;
426+
318427 // The tests above use promises to ensure ordering, but the tests below this call Plotly.animate
319428 // without chaining promises which would result in race conditions. This is not invalid behavior,
320429 // but it doesn't ensure proper ordering and completion, so these must be performed with finite
0 commit comments