@@ -33,14 +33,17 @@ function getAnimationDurationInMillis(element: Element): number {
33
33
* This `onStart` method is supposed to trigger a CSS animation (typically by adding a CSS class to the element, which defines
34
34
* an animation). Then the duration of the animation is automatically obtained from the element. When the `animationend`
35
35
* DOM event is emitted by the element or, by default (in case the event is never emitted), 20 milliseconds after the
36
- * scheduled end of the animation, the `onEnd` method of the animation, if any, is called. This `onEnd` method typically removes
37
- * the CSS class, so that the animation can be re-executed again later.
36
+ * scheduled end of the animation, the `onEnd` method of the animation, if any, is called.
37
+ * This `onEnd` method typically removes the CSS class, so that the animation can be re-executed again later.
38
+ * Then, the observable emits and completes to signal the end of the animation.
39
+ * If the returned observable is unsubscribed before it has emitted, the `onEnd` method is also called.
38
40
* @param element the element to animate
39
41
* @param animation the animation to apply on the element
40
42
* @param synchronous if true, then the `onEnd` method of the animation is called synchronously, immediately
41
- * after the `onStart` method. This can be useful in tests, when you do not want the animation to take any time.
42
- * The returned observable, in that case, also emits and completes synchronously, as soon as it is subscribed.
43
- * @return an Observable which emits a single time and then completes, once the animation is done.
43
+ * after the `onStart` method, and the returned observable also emits and completes synchronously.
44
+ * This can be useful in tests, when you do not want the animation to take any time.
45
+ * @return an Observable which emits a single time and then completes, once the animation is done and the `onEnd` method has been
46
+ * called.
44
47
*/
45
48
export function animate (
46
49
element : HTMLElement ,
@@ -50,8 +53,16 @@ export function animate(
50
53
return new Observable ( observer => {
51
54
animation . onStart ( element ) ;
52
55
const onEnd = animation . onEnd ?? ( ( ) => { } ) ;
56
+ let onEndCalled = false ;
57
+ const terminate = ( ) => {
58
+ if ( ! onEndCalled ) {
59
+ onEndCalled = true ;
60
+ onEnd ( element ) ;
61
+ }
62
+ } ;
53
63
let subscription : Subscription | null = null ;
54
64
if ( synchronous ) {
65
+ terminate ( ) ;
55
66
observer . next ( ) ;
56
67
observer . complete ( ) ;
57
68
} else {
@@ -64,14 +75,15 @@ export function animate(
64
75
// emits once some time after the animation end, just in case the animationend event is not emitted
65
76
const timeElapsed$ = timer ( animationDuration + 20 ) ;
66
77
subscription = race ( animationEnd$ , timeElapsed$ ) . subscribe ( ( ) => {
78
+ terminate ( ) ;
67
79
observer . next ( undefined ) ;
68
80
observer . complete ( ) ;
69
81
} ) ;
70
82
}
71
83
72
84
return ( ) => {
73
85
subscription ?. unsubscribe ( ) ;
74
- onEnd ( element ) ;
86
+ terminate ( ) ;
75
87
} ;
76
88
} ) ;
77
89
}
0 commit comments