From 7ae4c0cc77ad5fa36171960c7a2a1729f6ca0dcb Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 30 Jan 2018 17:30:12 +0100 Subject: [PATCH 1/3] feat(ripple): support ripple fade-out on pointer up By default, ripples in Angular Material will only fade out if the pointer is released and the enter animation completed. This behavior is similar in the MDC implementation of the Material Design guidelines. In some scenarios, developers prefer fading out the ripples immediately on pointer release. Similarly the Material Design guidelines also have some videos that show ripples that seem to follow that behavior as well. To be able to provide the necessary flexibility (as we already did with the animation durations), the `terminateOnPointerUp` global option has been added. Closes #9577 --- src/lib/core/ripple/ripple-renderer.ts | 10 ++++++++-- src/lib/core/ripple/ripple.spec.ts | 21 +++++++++++++++++++++ src/lib/core/ripple/ripple.ts | 7 +++++++ src/lib/tabs/tab-nav-bar/tab-nav-bar.ts | 1 + 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/lib/core/ripple/ripple-renderer.ts b/src/lib/core/ripple/ripple-renderer.ts index 9a42d14f1e56..de0d2e6428b6 100644 --- a/src/lib/core/ripple/ripple-renderer.ts +++ b/src/lib/core/ripple/ripple-renderer.ts @@ -15,6 +15,7 @@ export type RippleConfig = { radius?: number; persistent?: boolean; animation?: RippleAnimationConfig; + terminateOnPointerUp?: boolean; /** @deprecated Use the animation property instead. */ speedFactor?: number; }; @@ -244,9 +245,14 @@ export class RippleRenderer { this._isPointerDown = false; - // Fade-out all ripples that are completely visible and not persistent. + // Fade-out all ripples that are visible and not persistent. this._activeRipples.forEach(ripple => { - if (!ripple.config.persistent && ripple.state === RippleState.VISIBLE) { + // By default, only ripples that are completely visible will fade out on pointer release. + // If the `terminateOnPointerUp` option is set, ripples that fade in will also fade out. + const isVisible = ripple.state === RippleState.VISIBLE || + ripple.config.terminateOnPointerUp && ripple.state === RippleState.FADING_IN; + + if (!ripple.config.persistent && isVisible) { ripple.fadeOut(); } }); diff --git a/src/lib/core/ripple/ripple.spec.ts b/src/lib/core/ripple/ripple.spec.ts index 5f2734da3661..40a7282cdd01 100644 --- a/src/lib/core/ripple/ripple.spec.ts +++ b/src/lib/core/ripple/ripple.spec.ts @@ -500,6 +500,27 @@ describe('MatRipple', () => { expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); })); + + it('should allow ripples to fade out immediately on pointer up', fakeAsync(() => { + createTestComponent({ + terminateOnPointerUp: true + }); + + dispatchMouseEvent(rippleTarget, 'mousedown'); + dispatchMouseEvent(rippleTarget, 'mouseup'); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1); + + // Ignore the enter duration, because we immediately fired the mouseup after the mousedown. + // This means that the ripple should just fade out, and there shouldn't be an enter animation. + tick(exitDuration); + + expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0); + + // Since the enter duration is bigger than the exit duration, the enter duration timer + // will still exist. To properly finish all timers, we just wait the remaining time. + tick (enterDuration - exitDuration); + })); }); describe('configuring behavior', () => { diff --git a/src/lib/core/ripple/ripple.ts b/src/lib/core/ripple/ripple.ts index 52c2d693f0fa..f229d53e4835 100644 --- a/src/lib/core/ripple/ripple.ts +++ b/src/lib/core/ripple/ripple.ts @@ -42,6 +42,12 @@ export interface RippleGlobalOptions { * @deprecated Use the `animation` global option instead. */ baseSpeedFactor?: number; + + /** + * Whether ripples should start fading out immediately after the mouse our touch is released. By + * default, ripples will wait for the enter animation to complete and for mouse or touch release. + */ + terminateOnPointerUp?: boolean; } /** Injection token that can be used to specify the global ripple options. */ @@ -159,6 +165,7 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget { radius: this.radius, color: this.color, animation: {...this._globalOptions.animation, ...this.animation}, + terminateOnPointerUp: this._globalOptions.terminateOnPointerUp, speedFactor: this.speedFactor * (this._globalOptions.baseSpeedFactor || 1), }; } diff --git a/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts b/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts index 534a5a3f1ad5..4838eb9e5b31 100644 --- a/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts +++ b/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts @@ -240,6 +240,7 @@ export class MatTabLink extends _MatTabLinkMixinBase if (globalOptions) { this.rippleConfig = { + terminateOnPointerUp: globalOptions.terminateOnPointerUp, speedFactor: globalOptions.baseSpeedFactor, animation: globalOptions.animation, }; From b862fce4ee032cf1b1f220afd2f8c818dd16afee Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Tue, 30 Jan 2018 17:36:33 +0100 Subject: [PATCH 2/3] Comment improvements --- src/lib/core/ripple/ripple-renderer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/core/ripple/ripple-renderer.ts b/src/lib/core/ripple/ripple-renderer.ts index de0d2e6428b6..1a70627f748a 100644 --- a/src/lib/core/ripple/ripple-renderer.ts +++ b/src/lib/core/ripple/ripple-renderer.ts @@ -247,8 +247,8 @@ export class RippleRenderer { // Fade-out all ripples that are visible and not persistent. this._activeRipples.forEach(ripple => { - // By default, only ripples that are completely visible will fade out on pointer release. - // If the `terminateOnPointerUp` option is set, ripples that fade in will also fade out. + // By default, only ripples that are completely visible will fade out on pointer release. + // If the `terminateOnPointerUp` option is set, ripples that still fade in will also fade out. const isVisible = ripple.state === RippleState.VISIBLE || ripple.config.terminateOnPointerUp && ripple.state === RippleState.FADING_IN; From cbd7be04bef1c94c113c4edf6e900e89683e456c Mon Sep 17 00:00:00 2001 From: Paul Gschwendtner Date: Wed, 31 Jan 2018 15:12:20 +0100 Subject: [PATCH 3/3] Remove unnecessary whitespace --- src/lib/core/ripple/ripple.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/core/ripple/ripple.spec.ts b/src/lib/core/ripple/ripple.spec.ts index 40a7282cdd01..618796bd8745 100644 --- a/src/lib/core/ripple/ripple.spec.ts +++ b/src/lib/core/ripple/ripple.spec.ts @@ -519,7 +519,7 @@ describe('MatRipple', () => { // Since the enter duration is bigger than the exit duration, the enter duration timer // will still exist. To properly finish all timers, we just wait the remaining time. - tick (enterDuration - exitDuration); + tick(enterDuration - exitDuration); })); });