Skip to content

Commit

Permalink
feat(ripple): Implement new ripple sizing requirements (#244)
Browse files Browse the repository at this point in the history
Resolves #187
  • Loading branch information
cristobalchao committed Feb 2, 2017
1 parent 18b2056 commit f0d26e6
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 115 deletions.
2 changes: 1 addition & 1 deletion demos/ripple.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
-webkit-user-select: none;
}

.demo-surface[data-mdc-ripple-is-unbounded] {
.mdc-ripple-surface[data-mdc-ripple-is-unbounded] {
width: 40px;
height: 40px;
padding: 0;
Expand Down
6 changes: 3 additions & 3 deletions packages/mdc-ripple/_keyframes.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@

@keyframes mdc-ripple-fg-radius-in {
from {
transform: scale(0);
transform: scale(1);
animation-timing-function: $mdc-ripple-easing-fn;
}

to {
transform: scale(1.5);
transform: scale(var(--mdc-ripple-fg-scale));
}
}

Expand Down Expand Up @@ -56,6 +56,6 @@
}

to {
transform: scale(1.01);
transform: scale(var(--mdc-ripple-fg-scale));
}
}
16 changes: 8 additions & 8 deletions packages/mdc-ripple/_mixins.scss
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
//
//
// Copyright 2016 Google Inc. All Rights Reserved.
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//
// http://www.apache.org/licenses/LICENSE-2.0
//
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//

@import "@material/theme/variables";
@import "./keyframes";
Expand Down Expand Up @@ -78,7 +78,7 @@
left: calc(50% - #{$radius});
width: $radius * 2;
height: $radius * 2;
transform: scale(1);
transform: scale(var(--mdc-ripple-fg-scale));
transition: opacity 200ms linear;
border-radius: 50%;
opacity: 0;
Expand Down Expand Up @@ -163,7 +163,7 @@
&.mdc-ripple-upgraded--unbounded#{$pseudo} {
top: var(--mdc-ripple-top);
left: var(--mdc-ripple-left);
transform-origin: var(--mdc-ripple-xfo-x) var(--mdc-ripple-xfo-y);
transform-origin: center center;
}

&.mdc-ripple-upgraded--foreground-bounded-active-fill#{$pseudo} {
Expand All @@ -172,7 +172,7 @@
}

&.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded--foreground-unbounded-activation#{$pseudo} {
transform: scale(1);
transform: scale(var(--mdc-ripple-fg-scale));
transition:
opacity 110ms linear,
transform var(--mdc-ripple-fg-unbounded-transform-duration) linear 80ms;
Expand Down
5 changes: 3 additions & 2 deletions packages/mdc-ripple/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@ export const strings = {
VAR_FG_UNBOUNDED_TRANSFORM_DURATION: `--${ROOT}-fg-unbounded-transform-duration`,
VAR_LEFT: `--${ROOT}-left`,
VAR_TOP: `--${ROOT}-top`,
VAR_XF_ORIGIN_X: `--${ROOT}-xfo-x`,
VAR_XF_ORIGIN_Y: `--${ROOT}-xfo-y`,
VAR_FG_APPROX_XF: `--${ROOT}-fg-approx-xf`,
VAR_FG_SCALE: `--${ROOT}-fg-scale`,
};

export const numbers = {
Expand All @@ -49,4 +48,6 @@ export const numbers = {
ACTIVE_OPACITY_DURATION_MS: 110,
MIN_OPACITY_DURATION_MS: 200,
UNBOUNDED_TRANSFORM_DURATION_MS: 200,
PADDING: 10,
INITIAL_ORIGIN_SCALE: 0.6,
};
44 changes: 19 additions & 25 deletions packages/mdc-ripple/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ export default class MDCRippleFoundation extends MDCFoundation {
this.frame_ = {width: 0, height: 0};
this.activationState_ = this.defaultActivationState_();
this.xfDuration_ = 0;
this.maxRadius = 0;
this.initialSize_ = 0;
this.maxRadius_ = 0;
this.listenerInfos_ = [
{activate: 'touchstart', deactivate: 'touchend'},
{activate: 'pointerdown', deactivate: 'pointerup'},
Expand All @@ -99,6 +100,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
left: 0,
top: 0,
};
this.fgScale_ = 0;
}

defaultActivationState_() {
Expand Down Expand Up @@ -193,22 +195,6 @@ export default class MDCRippleFoundation extends MDCFoundation {

animateUnboundedActivation_() {
const {FG_UNBOUNDED_ACTIVATION} = MDCRippleFoundation.cssClasses;
let startPoint;
if (this.activationState_.wasActivatedByPointer) {
startPoint = getNormalizedEventCoords(
this.activationState_.activationEvent, this.adapter_.getWindowPageOffset(),
this.adapter_.computeBoundingRect()
);
} else {
startPoint = {
left: this.frame_.width / 2,
top: this.frame_.height / 2,
};
}
const {left, top} = startPoint;
const {VAR_XF_ORIGIN_X, VAR_XF_ORIGIN_Y} = MDCRippleFoundation.strings;
this.adapter_.updateCssVariable(VAR_XF_ORIGIN_X, `${left - this.unboundedCoords_.left}px`);
this.adapter_.updateCssVariable(VAR_XF_ORIGIN_Y, `${top - this.unboundedCoords_.top}px`);
this.adapter_.addClass(FG_UNBOUNDED_ACTIVATION);
}

Expand Down Expand Up @@ -285,7 +271,8 @@ export default class MDCRippleFoundation extends MDCFoundation {

let approxCurScale = 0;
if (msElapsed > FG_TRANSFORM_DELAY_MS) {
approxCurScale = Math.min((msElapsed - FG_TRANSFORM_DELAY_MS) / this.xfDuration_, 1);
const percentComplete = Math.min((msElapsed - FG_TRANSFORM_DELAY_MS) / this.xfDuration_, 1);
approxCurScale = percentComplete * this.fgScale_;
}

const transformDuration = UNBOUNDED_TRANSFORM_DURATION_MS;
Expand Down Expand Up @@ -368,30 +355,37 @@ export default class MDCRippleFoundation extends MDCFoundation {
this.frame_ = this.adapter_.computeBoundingRect();

const maxDim = Math.max(this.frame_.height, this.frame_.width);
const surfaceDiameter = Math.sqrt(Math.pow(this.frame_.width, 2) + Math.pow(this.frame_.height, 2));

// Sqrt(2) * square length == diameter
this.maxRadius_ = Math.sqrt(2) * maxDim / 2;
// 60% of the largest dimension of the surface
this.initialSize_ = maxDim * MDCRippleFoundation.numbers.INITIAL_ORIGIN_SCALE;

// Diameter of the surface + 10px
this.maxRadius_ = surfaceDiameter + MDCRippleFoundation.numbers.PADDING;
this.fgScale_ = this.maxRadius_ / this.initialSize_;
this.xfDuration_ = 1000 * Math.sqrt(this.maxRadius_ / 1024);
this.updateLayoutCssVars_();
}

updateLayoutCssVars_() {
const fgSize = this.maxRadius_ * 2;
const {
VAR_SURFACE_WIDTH, VAR_SURFACE_HEIGHT, VAR_FG_SIZE,
VAR_FG_UNBOUNDED_TRANSFORM_DURATION, VAR_LEFT, VAR_TOP,
VAR_FG_UNBOUNDED_TRANSFORM_DURATION, VAR_LEFT, VAR_TOP, VAR_FG_SCALE,
} = MDCRippleFoundation.strings;

this.adapter_.updateCssVariable(VAR_SURFACE_WIDTH, `${this.frame_.width}px`);
this.adapter_.updateCssVariable(VAR_SURFACE_HEIGHT, `${this.frame_.height}px`);
this.adapter_.updateCssVariable(VAR_FG_SIZE, `${fgSize}px`);
this.adapter_.updateCssVariable(VAR_FG_SIZE, `${this.initialSize_}px`);
this.adapter_.updateCssVariable(VAR_FG_UNBOUNDED_TRANSFORM_DURATION, `${this.xfDuration_}ms`);
this.adapter_.updateCssVariable(VAR_FG_SCALE, this.fgScale_);

if (this.adapter_.isUnbounded()) {
this.unboundedCoords_ = {
left: Math.round(-(fgSize / 2) + (this.frame_.width / 2)),
top: Math.round(-(fgSize / 2) + (this.frame_.height / 2)),
left: Math.round((this.frame_.width / 2) - (this.initialSize_ / 2)),
top: Math.round((this.frame_.height / 2) - (this.initialSize_ / 2)),
};


this.adapter_.updateCssVariable(VAR_LEFT, `${this.unboundedCoords_.left}px`);
this.adapter_.updateCssVariable(VAR_TOP, `${this.unboundedCoords_.top}px`);
}
Expand Down
52 changes: 1 addition & 51 deletions test/unit/mdc-ripple/foundation-activation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import td from 'testdouble';

import {testFoundation, captureHandlers} from './helpers';
import {cssClasses, strings} from '../../../packages/mdc-ripple/constants';
import {cssClasses} from '../../../packages/mdc-ripple/constants';

testFoundation(`adds ${cssClasses.BG_ACTIVE} on mousedown`, (t) => {
const {foundation, adapter, mockRaf} = t.data;
Expand Down Expand Up @@ -131,53 +131,3 @@ testFoundation('displays the foreground ripple on activation when unbounded', (t
t.end();
});

testFoundation('sets unbounded FG xf origin to the coords within surface on pointer activation, ' +
'accounting for FG ripple offset', (t) => {
const {foundation, adapter, mockRaf} = t.data;
const handlers = captureHandlers(adapter);
td.when(adapter.computeBoundingRect()).thenReturn({width: 100, height: 100, left: 50, top: 50});
td.when(adapter.isUnbounded()).thenReturn(true);
foundation.init();
mockRaf.flush();

handlers.mousedown({pageX: 100, pageY: 75});
mockRaf.flush();

t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_X, '71px')));
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_Y, '46px')));
t.end();
});

testFoundation('takes scroll offset into account when computing transform origin', (t) => {
const {foundation, adapter, mockRaf} = t.data;
const handlers = captureHandlers(adapter);
td.when(adapter.computeBoundingRect()).thenReturn({width: 100, height: 100, left: 25, top: 25});
td.when(adapter.getWindowPageOffset()).thenReturn({x: 25, y: 25});
td.when(adapter.isUnbounded()).thenReturn(true);
foundation.init();
mockRaf.flush();

handlers.mousedown({pageX: 100, pageY: 75});
mockRaf.flush();

t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_X, '71px')));
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_Y, '46px')));
t.end();
});

testFoundation('sets unbounded FG xf origin to center on non-pointer activation', (t) => {
const {foundation, adapter, mockRaf} = t.data;
const handlers = captureHandlers(adapter);
td.when(adapter.computeBoundingRect()).thenReturn({width: 100, height: 100, left: 50, top: 50});
td.when(adapter.isUnbounded()).thenReturn(true);
td.when(adapter.isSurfaceActive()).thenReturn(true);
foundation.init();
mockRaf.flush();

handlers.keydown();
mockRaf.flush();

t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_X, '71px')));
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_Y, '71px')));
t.end();
});
10 changes: 7 additions & 3 deletions test/unit/mdc-ripple/foundation-deactivation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,9 @@ testFoundation('triggers unbounded deactivation based on time it took to activat
const clock = lolex.install();
const {foundation, adapter, mockRaf} = t.data;
const handlers = captureHandlers(adapter);
const size = 100;
td.when(adapter.isUnbounded()).thenReturn(true);
td.when(adapter.computeBoundingRect()).thenReturn({width: 100, height: 100, left: 0, top: 0});
td.when(adapter.computeBoundingRect()).thenReturn({width: size, height: size, left: 0, top: 0});
foundation.init();
mockRaf.flush();

Expand All @@ -210,10 +211,13 @@ testFoundation('triggers unbounded deactivation based on time it took to activat
handlers.mouseup();
mockRaf.flush();

const maxRadius = Math.sqrt(2) * 50;
const surfaceDiameter = Math.sqrt(Math.pow(size, 2) + Math.pow(size, 2));
const initialSize = size * numbers.INITIAL_ORIGIN_SCALE;
const maxRadius = surfaceDiameter + numbers.PADDING;
const fgScale = maxRadius / initialSize;
const xfDuration = 1000 * Math.sqrt(maxRadius / 1024);

const scaleVal = baseElapsedTime / xfDuration;
const scaleVal = baseElapsedTime / xfDuration * fgScale;
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_FG_APPROX_XF, `scale(${scaleVal})`)));
t.doesNotThrow(
() => td.verify(
Expand Down

0 comments on commit f0d26e6

Please sign in to comment.