@@ -171,21 +171,15 @@ export class RippleRenderer {
171171 this . _mostRecentTransientRipple = rippleRef ;
172172 }
173173
174- // Wait for the ripple element to be completely faded in.
175- // Once it's faded in, the ripple can be hidden immediately if the mouse is released.
176- this . runTimeoutOutsideZone ( ( ) => {
177- const isMostRecentTransientRipple = rippleRef === this . _mostRecentTransientRipple ;
178-
179- rippleRef . state = RippleState . VISIBLE ;
174+ this . _ngZone . runOutsideAngular ( ( ) => {
175+ ripple . addEventListener ( 'transitionend' , ( ) => this . _onRippleTransitionEnd ( rippleRef ) ) ;
176+ } ) ;
180177
181- // When the timer runs out while the user has kept their pointer down, we want to
182- // keep only the persistent ripples and the latest transient ripple. We do this,
183- // because we don't want stacked transient ripples to appear after their enter
184- // animation has finished.
185- if ( ! config . persistent && ( ! isMostRecentTransientRipple || ! this . _isPointerDown ) ) {
186- rippleRef . fadeOut ( ) ;
187- }
188- } , duration ) ;
178+ // In case there is no fade-in transition duration, we need to manually call the
179+ // transition end listener because `transitionend` doesn't fire if there is no transition.
180+ if ( ! duration ) {
181+ this . _onRippleTransitionEnd ( rippleRef ) ;
182+ }
189183
190184 return rippleRef ;
191185 }
@@ -211,15 +205,17 @@ export class RippleRenderer {
211205 const rippleEl = rippleRef . element ;
212206 const animationConfig = { ...defaultRippleAnimationConfig , ...rippleRef . config . animation } ;
213207
208+ // This starts the fade-out transition and will fire the transition end listener that
209+ // removes the ripple element from the DOM.
214210 rippleEl . style . transitionDuration = `${ animationConfig . exitDuration } ms` ;
215211 rippleEl . style . opacity = '0' ;
216212 rippleRef . state = RippleState . FADING_OUT ;
217213
218- // Once the ripple faded out, the ripple can be safely removed from the DOM.
219- this . runTimeoutOutsideZone ( ( ) => {
220- rippleRef . state = RippleState . HIDDEN ;
221- rippleEl . parentNode ! . removeChild ( rippleEl ) ;
222- } , animationConfig . exitDuration ) ;
214+ // In case there is no fade- out transition duration, we need to manually call the
215+ // transition end listener because `transitionend` doesn't fire if there is no transition.
216+ if ( ! animationConfig . exitDuration ) {
217+ this . _onRippleTransitionEnd ( rippleRef ) ;
218+ }
223219 }
224220
225221 /** Fades out all currently active ripples. */
@@ -244,6 +240,28 @@ export class RippleRenderer {
244240 this . _triggerElement = element ;
245241 }
246242
243+ /** Transition end event listener that fires after ripples fade in or fade out. */
244+ private _onRippleTransitionEnd ( rippleRef : RippleRef ) {
245+ const { config, element} = rippleRef ;
246+
247+ if ( rippleRef . state === RippleState . FADING_IN ) {
248+ const isMostRecentTransientRipple = rippleRef === this . _mostRecentTransientRipple ;
249+
250+ rippleRef . state = RippleState . VISIBLE ;
251+
252+ // When the timer runs out while the user has kept their pointer down, we want to
253+ // keep only the persistent ripples and the latest transient ripple. We do this,
254+ // because we don't want stacked transient ripples to appear after their enter
255+ // animation has finished.
256+ if ( ! config . persistent && ( ! isMostRecentTransientRipple || ! this . _isPointerDown ) ) {
257+ rippleRef . fadeOut ( ) ;
258+ }
259+ } else if ( rippleRef . state === RippleState . FADING_OUT ) {
260+ rippleRef . state = RippleState . HIDDEN ;
261+ element . parentNode ! . removeChild ( element ) ;
262+ }
263+ }
264+
247265 /** Function being called whenever the trigger is being pressed using mouse. */
248266 private onMousedown = ( event : MouseEvent ) => {
249267 const isSyntheticEvent = this . _lastTouchStartEvent &&
@@ -290,11 +308,6 @@ export class RippleRenderer {
290308 } ) ;
291309 }
292310
293- /** Runs a timeout outside of the Angular zone to avoid triggering the change detection. */
294- private runTimeoutOutsideZone ( fn : Function , delay = 0 ) {
295- this . _ngZone . runOutsideAngular ( ( ) => setTimeout ( fn , delay ) ) ;
296- }
297-
298311 /** Removes previously registered event listeners from the trigger element. */
299312 _removeTriggerEvents ( ) {
300313 if ( this . _triggerElement ) {
0 commit comments