11import { ElementRef , NgZone } from '@angular/core' ;
22import { ViewportRuler } from '../overlay/position/viewport-ruler' ;
3+ import { RippleRef } from './ripple-ref' ;
34
45/** Fade-in duration for the ripples. Can be modified with the speedFactor option. */
56export const RIPPLE_FADE_IN_DURATION = 450 ;
67
78/** Fade-out duration for the ripples in milliseconds. This can't be modified by the speedFactor. */
89export const RIPPLE_FADE_OUT_DURATION = 400 ;
910
10- /**
11- * Returns the distance from the point (x, y) to the furthest corner of a rectangle.
12- */
13- const distanceToFurthestCorner = ( x : number , y : number , rect : ClientRect ) => {
14- const distX = Math . max ( Math . abs ( x - rect . left ) , Math . abs ( x - rect . right ) ) ;
15- const distY = Math . max ( Math . abs ( y - rect . top ) , Math . abs ( y - rect . bottom ) ) ;
16- return Math . sqrt ( distX * distX + distY * distY ) ;
17- } ;
18-
1911export type RippleConfig = {
2012 color ?: string ;
2113 centered ?: boolean ;
2214 radius ?: number ;
2315 speedFactor ?: number ;
16+ persistent ?: boolean ;
2417} ;
2518
2619/**
@@ -41,12 +34,12 @@ export class RippleRenderer {
4134 /** Whether the mouse is currently down or not. */
4235 private _isMousedown : boolean = false ;
4336
44- /** Currently active ripples that will be closed on mouseup. */
45- private _activeRipples : HTMLElement [ ] = [ ] ;
46-
4737 /** Events to be registered on the trigger element. */
4838 private _triggerEvents = new Map < string , any > ( ) ;
4939
40+ /** Set of currently active ripple references. */
41+ private _activeRipples = new Set < RippleRef > ( ) ;
42+
5043 /** Ripple config for all ripples created by events. */
5144 rippleConfig : RippleConfig = { } ;
5245
@@ -66,7 +59,7 @@ export class RippleRenderer {
6659 }
6760
6861 /** Fades in a ripple at the given coordinates. */
69- fadeInRipple ( pageX : number , pageY : number , config : RippleConfig = { } ) {
62+ fadeInRipple ( pageX : number , pageY : number , config : RippleConfig = { } ) : RippleRef {
7063 let containerRect = this . _containerElement . getBoundingClientRect ( ) ;
7164
7265 if ( config . centered ) {
@@ -101,28 +94,46 @@ export class RippleRenderer {
10194
10295 // By default the browser does not recalculate the styles of dynamically created
10396 // ripple elements. This is critical because then the `scale` would not animate properly.
104- this . _enforceStyleRecalculation ( ripple ) ;
97+ enforceStyleRecalculation ( ripple ) ;
10598
10699 ripple . style . transform = 'scale(1)' ;
107100
108- // Wait for the ripple to be faded in. Once it's faded in, the ripple can be hidden immediately
109- // if the mouse is released.
101+ // Exposed reference to the ripple that will be returned.
102+ let rippleRef = new RippleRef ( this , ripple , config ) ;
103+
104+ // Wait for the ripple element to be completely faded in.
105+ // Once it's faded in, the ripple can be hidden immediately if the mouse is released.
110106 this . runTimeoutOutsideZone ( ( ) => {
111- this . _isMousedown ? this . _activeRipples . push ( ripple ) : this . fadeOutRipple ( ripple ) ;
107+ if ( config . persistent || this . _isMousedown ) {
108+ this . _activeRipples . add ( rippleRef ) ;
109+ } else {
110+ rippleRef . fadeOut ( ) ;
111+ }
112112 } , duration ) ;
113+
114+ return rippleRef ;
113115 }
114116
115- /** Fades out a ripple element. */
116- fadeOutRipple ( ripple : HTMLElement ) {
117- ripple . style . transitionDuration = `${ RIPPLE_FADE_OUT_DURATION } ms` ;
118- ripple . style . opacity = '0' ;
117+ /** Fades out a ripple reference. */
118+ fadeOutRipple ( ripple : RippleRef ) {
119+ let rippleEl = ripple . element ;
120+
121+ this . _activeRipples . delete ( ripple ) ;
122+
123+ rippleEl . style . transitionDuration = `${ RIPPLE_FADE_OUT_DURATION } ms` ;
124+ rippleEl . style . opacity = '0' ;
119125
120126 // Once the ripple faded out, the ripple can be safely removed from the DOM.
121127 this . runTimeoutOutsideZone ( ( ) => {
122- ripple . parentNode . removeChild ( ripple ) ;
128+ rippleEl . parentNode . removeChild ( rippleEl ) ;
123129 } , RIPPLE_FADE_OUT_DURATION ) ;
124130 }
125131
132+ /** Fades out all currently active ripples. */
133+ fadeOutAll ( ) {
134+ this . _activeRipples . forEach ( ripple => ripple . fadeOut ( ) ) ;
135+ }
136+
126137 /** Sets the trigger element and registers the mouse events. */
127138 setTriggerElement ( element : HTMLElement ) {
128139 // Remove all previously register event listeners from the trigger element.
@@ -151,8 +162,13 @@ export class RippleRenderer {
151162 /** Listener being called on mouseup event. */
152163 private onMouseup ( ) {
153164 this . _isMousedown = false ;
154- this . _activeRipples . forEach ( ripple => this . fadeOutRipple ( ripple ) ) ;
155- this . _activeRipples = [ ] ;
165+
166+ // On mouseup, fade-out all ripples that are active and not persistent.
167+ this . _activeRipples . forEach ( ripple => {
168+ if ( ! ripple . config . persistent ) {
169+ ripple . fadeOut ( ) ;
170+ }
171+ } ) ;
156172 }
157173
158174 /** Listener being called on mouseleave event. */
@@ -167,13 +183,22 @@ export class RippleRenderer {
167183 this . _ngZone . runOutsideAngular ( ( ) => setTimeout ( fn , delay ) ) ;
168184 }
169185
170- /** Enforces a style recalculation of a DOM element by computing its styles. */
171- // TODO(devversion): Move into global utility function.
172- private _enforceStyleRecalculation ( element : HTMLElement ) {
173- // Enforce a style recalculation by calling `getComputedStyle` and accessing any property.
174- // Calling `getPropertyValue` is important to let optimizers know that this is not a noop.
175- // See: https://gist.github.com/paulirish/5d52fb081b3570c81e3a
176- window . getComputedStyle ( element ) . getPropertyValue ( 'opacity' ) ;
177- }
186+ }
187+
188+ /** Enforces a style recalculation of a DOM element by computing its styles. */
189+ // TODO(devversion): Move into global utility function.
190+ function enforceStyleRecalculation ( element : HTMLElement ) {
191+ // Enforce a style recalculation by calling `getComputedStyle` and accessing any property.
192+ // Calling `getPropertyValue` is important to let optimizers know that this is not a noop.
193+ // See: https://gist.github.com/paulirish/5d52fb081b3570c81e3a
194+ window . getComputedStyle ( element ) . getPropertyValue ( 'opacity' ) ;
195+ }
178196
197+ /**
198+ * Returns the distance from the point (x, y) to the furthest corner of a rectangle.
199+ */
200+ function distanceToFurthestCorner ( x : number , y : number , rect : ClientRect ) {
201+ const distX = Math . max ( Math . abs ( x - rect . left ) , Math . abs ( x - rect . right ) ) ;
202+ const distY = Math . max ( Math . abs ( y - rect . top ) , Math . abs ( y - rect . bottom ) ) ;
203+ return Math . sqrt ( distX * distX + distY * distY ) ;
179204}
0 commit comments