Skip to content

Commit efb03c9

Browse files
devversiontinayuangao
authored andcommitted
feat(ripple): support ripple fade-out on pointer up (#9694)
* 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 * Comment improvements * Remove unnecessary whitespace
1 parent acbf3c8 commit efb03c9

File tree

4 files changed

+37
-2
lines changed

4 files changed

+37
-2
lines changed

src/lib/core/ripple/ripple-renderer.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export type RippleConfig = {
1515
radius?: number;
1616
persistent?: boolean;
1717
animation?: RippleAnimationConfig;
18+
terminateOnPointerUp?: boolean;
1819
/** @deprecated Use the animation property instead. */
1920
speedFactor?: number;
2021
};
@@ -244,9 +245,14 @@ export class RippleRenderer {
244245

245246
this._isPointerDown = false;
246247

247-
// Fade-out all ripples that are completely visible and not persistent.
248+
// Fade-out all ripples that are visible and not persistent.
248249
this._activeRipples.forEach(ripple => {
249-
if (!ripple.config.persistent && ripple.state === RippleState.VISIBLE) {
250+
// By default, only ripples that are completely visible will fade out on pointer release.
251+
// If the `terminateOnPointerUp` option is set, ripples that still fade in will also fade out.
252+
const isVisible = ripple.state === RippleState.VISIBLE ||
253+
ripple.config.terminateOnPointerUp && ripple.state === RippleState.FADING_IN;
254+
255+
if (!ripple.config.persistent && isVisible) {
250256
ripple.fadeOut();
251257
}
252258
});

src/lib/core/ripple/ripple.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -500,6 +500,27 @@ describe('MatRipple', () => {
500500

501501
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
502502
}));
503+
504+
it('should allow ripples to fade out immediately on pointer up', fakeAsync(() => {
505+
createTestComponent({
506+
terminateOnPointerUp: true
507+
});
508+
509+
dispatchMouseEvent(rippleTarget, 'mousedown');
510+
dispatchMouseEvent(rippleTarget, 'mouseup');
511+
512+
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(1);
513+
514+
// Ignore the enter duration, because we immediately fired the mouseup after the mousedown.
515+
// This means that the ripple should just fade out, and there shouldn't be an enter animation.
516+
tick(exitDuration);
517+
518+
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
519+
520+
// Since the enter duration is bigger than the exit duration, the enter duration timer
521+
// will still exist. To properly finish all timers, we just wait the remaining time.
522+
tick(enterDuration - exitDuration);
523+
}));
503524
});
504525

505526
describe('configuring behavior', () => {

src/lib/core/ripple/ripple.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,12 @@ export interface RippleGlobalOptions {
4242
* @deprecated Use the `animation` global option instead.
4343
*/
4444
baseSpeedFactor?: number;
45+
46+
/**
47+
* Whether ripples should start fading out immediately after the mouse our touch is released. By
48+
* default, ripples will wait for the enter animation to complete and for mouse or touch release.
49+
*/
50+
terminateOnPointerUp?: boolean;
4551
}
4652

4753
/** Injection token that can be used to specify the global ripple options. */
@@ -159,6 +165,7 @@ export class MatRipple implements OnInit, OnDestroy, RippleTarget {
159165
radius: this.radius,
160166
color: this.color,
161167
animation: {...this._globalOptions.animation, ...this.animation},
168+
terminateOnPointerUp: this._globalOptions.terminateOnPointerUp,
162169
speedFactor: this.speedFactor * (this._globalOptions.baseSpeedFactor || 1),
163170
};
164171
}

src/lib/tabs/tab-nav-bar/tab-nav-bar.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ export class MatTabLink extends _MatTabLinkMixinBase
240240

241241
if (globalOptions) {
242242
this.rippleConfig = {
243+
terminateOnPointerUp: globalOptions.terminateOnPointerUp,
243244
speedFactor: globalOptions.baseSpeedFactor,
244245
animation: globalOptions.animation,
245246
};

0 commit comments

Comments
 (0)