@@ -171,21 +171,15 @@ export class RippleRenderer {
171
171
this . _mostRecentTransientRipple = rippleRef ;
172
172
}
173
173
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
+ } ) ;
180
177
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
+ }
189
183
190
184
return rippleRef ;
191
185
}
@@ -211,15 +205,17 @@ export class RippleRenderer {
211
205
const rippleEl = rippleRef . element ;
212
206
const animationConfig = { ...defaultRippleAnimationConfig , ...rippleRef . config . animation } ;
213
207
208
+ // This starts the fade-out transition and will fire the transition end listener that
209
+ // removes the ripple element from the DOM.
214
210
rippleEl . style . transitionDuration = `${ animationConfig . exitDuration } ms` ;
215
211
rippleEl . style . opacity = '0' ;
216
212
rippleRef . state = RippleState . FADING_OUT ;
217
213
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
+ }
223
219
}
224
220
225
221
/** Fades out all currently active ripples. */
@@ -244,6 +240,28 @@ export class RippleRenderer {
244
240
this . _triggerElement = element ;
245
241
}
246
242
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
+
247
265
/** Function being called whenever the trigger is being pressed using mouse. */
248
266
private onMousedown = ( event : MouseEvent ) => {
249
267
const isSyntheticEvent = this . _lastTouchStartEvent &&
@@ -290,11 +308,6 @@ export class RippleRenderer {
290
308
} ) ;
291
309
}
292
310
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
-
298
311
/** Removes previously registered event listeners from the trigger element. */
299
312
_removeTriggerEvents ( ) {
300
313
if ( this . _triggerElement ) {
0 commit comments