From 6d5944613abe6487820f0cb13a0ae33a22411a1b Mon Sep 17 00:00:00 2001 From: Manu MA Date: Thu, 15 Nov 2018 16:08:39 +0100 Subject: [PATCH] fix(ripple-effect): follow MD spec (#16330) * fix(ripple-effect): follow md spec * add box-shadow back * add ripple effect to alert and action-sheet --- core/src/components.d.ts | 2 +- .../action-sheet/action-sheet.md.scss | 4 - .../components/action-sheet/action-sheet.tsx | 1 + core/src/components/alert/alert.md.scss | 4 - core/src/components/alert/alert.tsx | 2 +- core/src/components/button/button.ios.scss | 5 ++ core/src/components/button/button.md.scss | 1 + core/src/components/button/button.scss | 4 +- .../components/fab-button/fab-button.md.scss | 4 +- core/src/components/item/item.md.scss | 4 +- core/src/components/ripple-effect/readme.md | 4 +- .../ripple-effect/ripple-effect.scss | 51 ++++++++++--- .../ripple-effect/ripple-effect.tsx | 76 ++++++++++++------- core/src/utils/tap-click.ts | 6 +- 14 files changed, 109 insertions(+), 59 deletions(-) diff --git a/core/src/components.d.ts b/core/src/components.d.ts index ebc0ff8660..999ef1a898 100644 --- a/core/src/components.d.ts +++ b/core/src/components.d.ts @@ -3541,7 +3541,7 @@ export namespace Components { /** * Adds the ripple effect to the parent element */ - 'addRipple': (pageX: number, pageY: number) => void; + 'addRipple': (pageX: number, pageY: number) => Promise<() => void>; } interface IonRippleEffectAttributes extends StencilHTMLAttributes {} diff --git a/core/src/components/action-sheet/action-sheet.md.scss b/core/src/components/action-sheet/action-sheet.md.scss index da50b7a361..f87d7931e8 100644 --- a/core/src/components/action-sheet/action-sheet.md.scss +++ b/core/src/components/action-sheet/action-sheet.md.scss @@ -60,10 +60,6 @@ overflow: hidden; } -.action-sheet-button.activated { - background: $action-sheet-md-button-background-activated; -} - .action-sheet-icon { @include padding(null, null, 4px, null); @include margin($action-sheet-md-icon-margin-top, $action-sheet-md-icon-margin-end, $action-sheet-md-icon-margin-bottom, $action-sheet-md-icon-margin-start); diff --git a/core/src/components/action-sheet/action-sheet.tsx b/core/src/components/action-sheet/action-sheet.tsx index a7602fcb17..fb7656c9be 100644 --- a/core/src/components/action-sheet/action-sheet.tsx +++ b/core/src/components/action-sheet/action-sheet.tsx @@ -241,6 +241,7 @@ export class ActionSheet implements ComponentInterface, OverlayInterface { {b.icon && } {b.text} + {this.mode === 'md' && } )} diff --git a/core/src/components/alert/alert.md.scss b/core/src/components/alert/alert.md.scss index 28dc7057df..5f2ee1a296 100644 --- a/core/src/components/alert/alert.md.scss +++ b/core/src/components/alert/alert.md.scss @@ -290,10 +290,6 @@ overflow: hidden; } -.alert-button.activated { - background-color: $alert-md-button-background-color-activated; -} - .alert-button-inner { justify-content: $alert-md-button-group-justify-content; } diff --git a/core/src/components/alert/alert.tsx b/core/src/components/alert/alert.tsx index 9d7b419a52..6bac0364f8 100644 --- a/core/src/components/alert/alert.tsx +++ b/core/src/components/alert/alert.tsx @@ -369,7 +369,6 @@ export class Alert implements ComponentInterface, OverlayInterface { {i.label} - {this.mode === 'md' && } ))} @@ -429,6 +428,7 @@ export class Alert implements ComponentInterface, OverlayInterface { {button.text} + {this.mode === 'md' && } )} diff --git a/core/src/components/button/button.ios.scss b/core/src/components/button/button.ios.scss index 9e04406781..351373c7fa 100644 --- a/core/src/components/button/button.ios.scss +++ b/core/src/components/button/button.ios.scss @@ -26,6 +26,11 @@ // iOS Solid Button // -------------------------------------------------- + +:host(.button-solid) { + --background-activated: #{ion-color(primary, shade)}; +} + @media (any-hover: hover) { :host(.button-solid:hover) { --opacity: #{$button-ios-opacity-hover}; diff --git a/core/src/components/button/button.md.scss b/core/src/components/button/button.md.scss index 20f70ccd0c..b16279a7e3 100644 --- a/core/src/components/button/button.md.scss +++ b/core/src/components/button/button.md.scss @@ -31,6 +31,7 @@ // -------------------------------------------------- :host(.button-solid) { + --background-activated: var(--background); --box-shadow: #{$button-md-box-shadow}; } diff --git a/core/src/components/button/button.scss b/core/src/components/button/button.scss index b6950840c1..9cfdc69ab5 100644 --- a/core/src/components/button/button.scss +++ b/core/src/components/button/button.scss @@ -73,7 +73,6 @@ // Default Solid Color :host(.button-solid) { --background: #{ion-color(primary, base)}; - --background-activated: #{ion-color(primary, shade)}; --background-focused: #{ion-color(primary, shade)}; --color: #{ion-color(primary, contrast)}; --color-activated: #{ion-color(primary, contrast)}; @@ -87,8 +86,7 @@ } // Focused/Activated Solid Button with Color -:host(.button-solid.ion-color.focused) .button-native, -:host(.button-solid.ion-color.activated) .button-native { +:host(.button-solid.ion-color.focused) .button-native { background: #{current-color(shade)}; } diff --git a/core/src/components/fab-button/fab-button.md.scss b/core/src/components/fab-button/fab-button.md.scss index 4d4648ef56..8757d76685 100755 --- a/core/src/components/fab-button/fab-button.md.scss +++ b/core/src/components/fab-button/fab-button.md.scss @@ -6,7 +6,7 @@ :host { --background: #{$fab-md-background-color}; - --background-activated: #{$fab-md-background-color-activated}; + --background-activated: var(--background); --background-focused: var(--background-activated); --color: #{$fab-md-text-color}; --color-activated: #{$fab-md-text-color}; @@ -45,4 +45,4 @@ :host(.fab-button-in-list) ::slotted(ion-icon) { font-size: $fab-md-list-button-icon-font-size; -} \ No newline at end of file +} diff --git a/core/src/components/item/item.md.scss b/core/src/components/item/item.md.scss index 220cb60bdc..a798a6ff41 100644 --- a/core/src/components/item/item.md.scss +++ b/core/src/components/item/item.md.scss @@ -8,13 +8,11 @@ :host { --min-height: #{$item-md-min-height}; --background: var(--ion-item-background, transparent); - --background-activated: #{$item-md-background-activated}; + --background-activated: var(--background); --border-color: #{$item-md-border-bottom-color}; --color: #{$item-md-color}; --transition: background-color 300ms cubic-bezier(.4, 0, .2, 1); --padding-start: #{$item-md-padding-start}; - --background: #{$item-md-background}; - --background-activated: #{$item-md-background-activated}; --color: #{$item-md-color}; --border-color: #{$item-md-border-bottom-color}; --inner-padding-end: #{$item-md-padding-end}; diff --git a/core/src/components/ripple-effect/readme.md b/core/src/components/ripple-effect/readme.md index adffbb0a3d..7c66c666d6 100644 --- a/core/src/components/ripple-effect/readme.md +++ b/core/src/components/ripple-effect/readme.md @@ -8,7 +8,7 @@ The ripple effect component adds the [Material Design ink ripple interaction eff ## Methods -### `addRipple(pageX: number, pageY: number) => void` +### `addRipple(pageX: number, pageY: number) => Promise<() => void>` Adds the ripple effect to the parent element @@ -21,7 +21,7 @@ Adds the ripple effect to the parent element #### Returns -Type: `void` +Type: `Promise<() => void>` diff --git a/core/src/components/ripple-effect/ripple-effect.scss b/core/src/components/ripple-effect/ripple-effect.scss index d45d1197d7..cfd26c9e89 100644 --- a/core/src/components/ripple-effect/ripple-effect.scss +++ b/core/src/components/ripple-effect/ripple-effect.scss @@ -4,6 +4,11 @@ // Material Design Ripple Effect // -------------------------------------------------- +$scale-duration: 225ms; +$fade-in-duration: 75ms; +$fade-out-duration: 150ms; +$opacity-duration: $fade-in-duration + $fade-out-duration; + :host { @include position(0, 0, 0, 0); @@ -23,23 +28,51 @@ contain: strict; opacity: 0; - animation-name: rippleAnimation; - animation-duration: 200ms; - animation-timing-function: ease-out; + animation: + $scale-duration rippleAnimation forwards, + $fade-in-duration fadeInAnimation forwards; + will-change: transform, opacity; pointer-events: none; } -@keyframes rippleAnimation { - 0% { - transform: scale(.1); +.fade-out { + transform: translate(var(--translate-end)) scale(var(--final-scale, 1)); + animation: $fade-out-duration fadeOutAnimation forwards; +} - opacity: .2; +@keyframes rippleAnimation { + from { + animation-timing-function: cubic-bezier(.4, 0, .2, 1); + + transform: scale(1); } - 100% { - transform: scale(1); + to { + transform: translate(var(--translate-end)) scale(var(--final-scale, 1)); + } +} + +@keyframes fadeInAnimation { + from { + animation-timing-function: linear; opacity: 0; } + + to { + opacity: 0.16; + } +} + +@keyframes fadeOutAnimation { + from { + animation-timing-function: linear; + + opacity: 0.16; + } + + to { + opacity: 0; + } } diff --git a/core/src/components/ripple-effect/ripple-effect.tsx b/core/src/components/ripple-effect/ripple-effect.tsx index 4514714b8d..d823ada6b2 100644 --- a/core/src/components/ripple-effect/ripple-effect.tsx +++ b/core/src/components/ripple-effect/ripple-effect.tsx @@ -1,7 +1,6 @@ import { Component, ComponentInterface, Element, Method, Prop, QueueApi } from '@stencil/core'; import { Config } from '../../interface'; -import { rIC } from '../../utils/helpers'; @Component({ tag: 'ion-ripple-effect', @@ -20,42 +19,61 @@ export class RippleEffect implements ComponentInterface { * Adds the ripple effect to the parent element */ @Method() - addRipple(pageX: number, pageY: number) { + async addRipple(pageX: number, pageY: number) { if (this.config.getBoolean('animated', true)) { - rIC(() => this.prepareRipple(pageX, pageY)); + return this.prepareRipple(pageX, pageY); } + return () => { return; }; } private prepareRipple(pageX: number, pageY: number) { - let x: number; - let y: number; - let size: number; + return new Promise<() => void>(resolve => { + this.queue.read(() => { + const rect = this.el.getBoundingClientRect(); + const width = rect.width; + const height = rect.height; + const hypotenuse = Math.sqrt(width * width + height * height); + const maxRadius = hypotenuse + PADDING; + const maxDim = Math.max(height, width); + const posX = pageX - rect.left; + const posY = pageY - rect.top; - this.queue.read(() => { - const rect = this.el.getBoundingClientRect(); - const width = rect.width; - const height = rect.height; - size = Math.min(Math.sqrt(width * width + height * height) * 2, MAX_RIPPLE_DIAMETER); - x = pageX - rect.left - (size * 0.5); - y = pageY - rect.top - (size * 0.5); - }); - this.queue.write(() => { - const div = this.win.document.createElement('div'); - div.classList.add('ripple-effect'); - const style = div.style; - const duration = Math.max(RIPPLE_FACTOR * Math.sqrt(size), MIN_RIPPLE_DURATION); - style.top = y + 'px'; - style.left = x + 'px'; - style.width = style.height = size + 'px'; - style.animationDuration = duration + 'ms'; + const initialSize = Math.floor(maxDim * INITIAL_ORIGIN_SCALE); + const finalScale = maxRadius / initialSize; + const x = posX - initialSize * 0.5; + const y = posY - initialSize * 0.5; + const moveX = width * 0.5 - posX; + const moveY = height * 0.5 - posY; - const container = this.el.shadowRoot || this.el; - container.appendChild(div); - setTimeout(() => div.remove(), duration + 50); + this.queue.write(() => { + const div = this.win.document.createElement('div'); + div.classList.add('ripple-effect'); + const style = div.style; + style.top = y + 'px'; + style.left = x + 'px'; + style.width = style.height = initialSize + 'px'; + style.setProperty('--final-scale', `${finalScale}`); + style.setProperty('--translate-end', `${moveX}px, ${moveY}px`); + + const container = this.el.shadowRoot || this.el; + container.appendChild(div); + setTimeout(() => { + resolve(() => { + removeRipple(div); + }); + }, 225 + 100); + }); + }); }); } } -const RIPPLE_FACTOR = 35; -const MIN_RIPPLE_DURATION = 260; -const MAX_RIPPLE_DIAMETER = 550; +function removeRipple(ripple: HTMLElement) { + ripple.classList.add('fade-out'); + setTimeout(() => { + ripple.remove(); + }, 200); +} + +const PADDING = 10; +const INITIAL_ORIGIN_SCALE = 0.5; diff --git a/core/src/utils/tap-click.ts b/core/src/utils/tap-click.ts index 5e20538091..cf4efb9b12 100644 --- a/core/src/utils/tap-click.ts +++ b/core/src/utils/tap-click.ts @@ -8,6 +8,7 @@ export function startTapClick(doc: Document) { let scrolling = false; let activatableEle: HTMLElement | undefined; + let activeRipple: Promise<() => void> | undefined; let activeDefer: any; const clearDefers = new WeakMap(); @@ -116,11 +117,14 @@ export function startTapClick(doc: Document) { const rippleEffect = getRippleEffect(el); if (rippleEffect && rippleEffect.addRipple) { - rippleEffect.addRipple(x, y); + activeRipple = rippleEffect.addRipple(x, y); } } function removeActivated(smooth: boolean) { + if (activeRipple !== undefined) { + activeRipple.then(remove => remove()); + } const active = activatableEle; if (!active) { return;