Skip to content
This repository was archived by the owner on Jan 13, 2025. It is now read-only.

Commit f0d26e6

Browse files
authoredFeb 2, 2017
feat(ripple): Implement new ripple sizing requirements (#244)
Resolves #187
1 parent 18b2056 commit f0d26e6

8 files changed

+105
-115
lines changed
 

‎demos/ripple.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
-webkit-user-select: none;
4141
}
4242

43-
.demo-surface[data-mdc-ripple-is-unbounded] {
43+
.mdc-ripple-surface[data-mdc-ripple-is-unbounded] {
4444
width: 40px;
4545
height: 40px;
4646
padding: 0;

‎packages/mdc-ripple/_keyframes.scss

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@
2020

2121
@keyframes mdc-ripple-fg-radius-in {
2222
from {
23-
transform: scale(0);
23+
transform: scale(1);
2424
animation-timing-function: $mdc-ripple-easing-fn;
2525
}
2626

2727
to {
28-
transform: scale(1.5);
28+
transform: scale(var(--mdc-ripple-fg-scale));
2929
}
3030
}
3131

@@ -56,6 +56,6 @@
5656
}
5757

5858
to {
59-
transform: scale(1.01);
59+
transform: scale(var(--mdc-ripple-fg-scale));
6060
}
6161
}

‎packages/mdc-ripple/_mixins.scss

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
//
1+
//
22
// Copyright 2016 Google Inc. All Rights Reserved.
3-
//
3+
//
44
// Licensed under the Apache License, Version 2.0 (the "License");
55
// you may not use this file except in compliance with the License.
66
// You may obtain a copy of the License at
7-
//
7+
//
88
// http://www.apache.org/licenses/LICENSE-2.0
9-
//
9+
//
1010
// Unless required by applicable law or agreed to in writing, software
1111
// distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
14-
//
14+
//
1515

1616
@import "@material/theme/variables";
1717
@import "./keyframes";
@@ -78,7 +78,7 @@
7878
left: calc(50% - #{$radius});
7979
width: $radius * 2;
8080
height: $radius * 2;
81-
transform: scale(1);
81+
transform: scale(var(--mdc-ripple-fg-scale));
8282
transition: opacity 200ms linear;
8383
border-radius: 50%;
8484
opacity: 0;
@@ -163,7 +163,7 @@
163163
&.mdc-ripple-upgraded--unbounded#{$pseudo} {
164164
top: var(--mdc-ripple-top);
165165
left: var(--mdc-ripple-left);
166-
transform-origin: var(--mdc-ripple-xfo-x) var(--mdc-ripple-xfo-y);
166+
transform-origin: center center;
167167
}
168168

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

174174
&.mdc-ripple-upgraded--unbounded.mdc-ripple-upgraded--foreground-unbounded-activation#{$pseudo} {
175-
transform: scale(1);
175+
transform: scale(var(--mdc-ripple-fg-scale));
176176
transition:
177177
opacity 110ms linear,
178178
transform var(--mdc-ripple-fg-unbounded-transform-duration) linear 80ms;

‎packages/mdc-ripple/constants.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,8 @@ export const strings = {
3838
VAR_FG_UNBOUNDED_TRANSFORM_DURATION: `--${ROOT}-fg-unbounded-transform-duration`,
3939
VAR_LEFT: `--${ROOT}-left`,
4040
VAR_TOP: `--${ROOT}-top`,
41-
VAR_XF_ORIGIN_X: `--${ROOT}-xfo-x`,
42-
VAR_XF_ORIGIN_Y: `--${ROOT}-xfo-y`,
4341
VAR_FG_APPROX_XF: `--${ROOT}-fg-approx-xf`,
42+
VAR_FG_SCALE: `--${ROOT}-fg-scale`,
4443
};
4544

4645
export const numbers = {
@@ -49,4 +48,6 @@ export const numbers = {
4948
ACTIVE_OPACITY_DURATION_MS: 110,
5049
MIN_OPACITY_DURATION_MS: 200,
5150
UNBOUNDED_TRANSFORM_DURATION_MS: 200,
51+
PADDING: 10,
52+
INITIAL_ORIGIN_SCALE: 0.6,
5253
};

‎packages/mdc-ripple/foundation.js

+19-25
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ export default class MDCRippleFoundation extends MDCFoundation {
7272
this.frame_ = {width: 0, height: 0};
7373
this.activationState_ = this.defaultActivationState_();
7474
this.xfDuration_ = 0;
75-
this.maxRadius = 0;
75+
this.initialSize_ = 0;
76+
this.maxRadius_ = 0;
7677
this.listenerInfos_ = [
7778
{activate: 'touchstart', deactivate: 'touchend'},
7879
{activate: 'pointerdown', deactivate: 'pointerup'},
@@ -99,6 +100,7 @@ export default class MDCRippleFoundation extends MDCFoundation {
99100
left: 0,
100101
top: 0,
101102
};
103+
this.fgScale_ = 0;
102104
}
103105

104106
defaultActivationState_() {
@@ -193,22 +195,6 @@ export default class MDCRippleFoundation extends MDCFoundation {
193195

194196
animateUnboundedActivation_() {
195197
const {FG_UNBOUNDED_ACTIVATION} = MDCRippleFoundation.cssClasses;
196-
let startPoint;
197-
if (this.activationState_.wasActivatedByPointer) {
198-
startPoint = getNormalizedEventCoords(
199-
this.activationState_.activationEvent, this.adapter_.getWindowPageOffset(),
200-
this.adapter_.computeBoundingRect()
201-
);
202-
} else {
203-
startPoint = {
204-
left: this.frame_.width / 2,
205-
top: this.frame_.height / 2,
206-
};
207-
}
208-
const {left, top} = startPoint;
209-
const {VAR_XF_ORIGIN_X, VAR_XF_ORIGIN_Y} = MDCRippleFoundation.strings;
210-
this.adapter_.updateCssVariable(VAR_XF_ORIGIN_X, `${left - this.unboundedCoords_.left}px`);
211-
this.adapter_.updateCssVariable(VAR_XF_ORIGIN_Y, `${top - this.unboundedCoords_.top}px`);
212198
this.adapter_.addClass(FG_UNBOUNDED_ACTIVATION);
213199
}
214200

@@ -285,7 +271,8 @@ export default class MDCRippleFoundation extends MDCFoundation {
285271

286272
let approxCurScale = 0;
287273
if (msElapsed > FG_TRANSFORM_DELAY_MS) {
288-
approxCurScale = Math.min((msElapsed - FG_TRANSFORM_DELAY_MS) / this.xfDuration_, 1);
274+
const percentComplete = Math.min((msElapsed - FG_TRANSFORM_DELAY_MS) / this.xfDuration_, 1);
275+
approxCurScale = percentComplete * this.fgScale_;
289276
}
290277

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

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

372-
// Sqrt(2) * square length == diameter
373-
this.maxRadius_ = Math.sqrt(2) * maxDim / 2;
360+
// 60% of the largest dimension of the surface
361+
this.initialSize_ = maxDim * MDCRippleFoundation.numbers.INITIAL_ORIGIN_SCALE;
362+
363+
// Diameter of the surface + 10px
364+
this.maxRadius_ = surfaceDiameter + MDCRippleFoundation.numbers.PADDING;
365+
this.fgScale_ = this.maxRadius_ / this.initialSize_;
374366
this.xfDuration_ = 1000 * Math.sqrt(this.maxRadius_ / 1024);
375367
this.updateLayoutCssVars_();
376368
}
377369

378370
updateLayoutCssVars_() {
379-
const fgSize = this.maxRadius_ * 2;
380371
const {
381372
VAR_SURFACE_WIDTH, VAR_SURFACE_HEIGHT, VAR_FG_SIZE,
382-
VAR_FG_UNBOUNDED_TRANSFORM_DURATION, VAR_LEFT, VAR_TOP,
373+
VAR_FG_UNBOUNDED_TRANSFORM_DURATION, VAR_LEFT, VAR_TOP, VAR_FG_SCALE,
383374
} = MDCRippleFoundation.strings;
384375

385376
this.adapter_.updateCssVariable(VAR_SURFACE_WIDTH, `${this.frame_.width}px`);
386377
this.adapter_.updateCssVariable(VAR_SURFACE_HEIGHT, `${this.frame_.height}px`);
387-
this.adapter_.updateCssVariable(VAR_FG_SIZE, `${fgSize}px`);
378+
this.adapter_.updateCssVariable(VAR_FG_SIZE, `${this.initialSize_}px`);
388379
this.adapter_.updateCssVariable(VAR_FG_UNBOUNDED_TRANSFORM_DURATION, `${this.xfDuration_}ms`);
380+
this.adapter_.updateCssVariable(VAR_FG_SCALE, this.fgScale_);
389381

390382
if (this.adapter_.isUnbounded()) {
391383
this.unboundedCoords_ = {
392-
left: Math.round(-(fgSize / 2) + (this.frame_.width / 2)),
393-
top: Math.round(-(fgSize / 2) + (this.frame_.height / 2)),
384+
left: Math.round((this.frame_.width / 2) - (this.initialSize_ / 2)),
385+
top: Math.round((this.frame_.height / 2) - (this.initialSize_ / 2)),
394386
};
387+
388+
395389
this.adapter_.updateCssVariable(VAR_LEFT, `${this.unboundedCoords_.left}px`);
396390
this.adapter_.updateCssVariable(VAR_TOP, `${this.unboundedCoords_.top}px`);
397391
}

‎test/unit/mdc-ripple/foundation-activation.test.js

+1-51
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import td from 'testdouble';
1818

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

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

134-
testFoundation('sets unbounded FG xf origin to the coords within surface on pointer activation, ' +
135-
'accounting for FG ripple offset', (t) => {
136-
const {foundation, adapter, mockRaf} = t.data;
137-
const handlers = captureHandlers(adapter);
138-
td.when(adapter.computeBoundingRect()).thenReturn({width: 100, height: 100, left: 50, top: 50});
139-
td.when(adapter.isUnbounded()).thenReturn(true);
140-
foundation.init();
141-
mockRaf.flush();
142-
143-
handlers.mousedown({pageX: 100, pageY: 75});
144-
mockRaf.flush();
145-
146-
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_X, '71px')));
147-
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_Y, '46px')));
148-
t.end();
149-
});
150-
151-
testFoundation('takes scroll offset into account when computing transform origin', (t) => {
152-
const {foundation, adapter, mockRaf} = t.data;
153-
const handlers = captureHandlers(adapter);
154-
td.when(adapter.computeBoundingRect()).thenReturn({width: 100, height: 100, left: 25, top: 25});
155-
td.when(adapter.getWindowPageOffset()).thenReturn({x: 25, y: 25});
156-
td.when(adapter.isUnbounded()).thenReturn(true);
157-
foundation.init();
158-
mockRaf.flush();
159-
160-
handlers.mousedown({pageX: 100, pageY: 75});
161-
mockRaf.flush();
162-
163-
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_X, '71px')));
164-
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_Y, '46px')));
165-
t.end();
166-
});
167-
168-
testFoundation('sets unbounded FG xf origin to center on non-pointer activation', (t) => {
169-
const {foundation, adapter, mockRaf} = t.data;
170-
const handlers = captureHandlers(adapter);
171-
td.when(adapter.computeBoundingRect()).thenReturn({width: 100, height: 100, left: 50, top: 50});
172-
td.when(adapter.isUnbounded()).thenReturn(true);
173-
td.when(adapter.isSurfaceActive()).thenReturn(true);
174-
foundation.init();
175-
mockRaf.flush();
176-
177-
handlers.keydown();
178-
mockRaf.flush();
179-
180-
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_X, '71px')));
181-
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_XF_ORIGIN_Y, '71px')));
182-
t.end();
183-
});

‎test/unit/mdc-ripple/foundation-deactivation.test.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,9 @@ testFoundation('triggers unbounded deactivation based on time it took to activat
195195
const clock = lolex.install();
196196
const {foundation, adapter, mockRaf} = t.data;
197197
const handlers = captureHandlers(adapter);
198+
const size = 100;
198199
td.when(adapter.isUnbounded()).thenReturn(true);
199-
td.when(adapter.computeBoundingRect()).thenReturn({width: 100, height: 100, left: 0, top: 0});
200+
td.when(adapter.computeBoundingRect()).thenReturn({width: size, height: size, left: 0, top: 0});
200201
foundation.init();
201202
mockRaf.flush();
202203

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

213-
const maxRadius = Math.sqrt(2) * 50;
214+
const surfaceDiameter = Math.sqrt(Math.pow(size, 2) + Math.pow(size, 2));
215+
const initialSize = size * numbers.INITIAL_ORIGIN_SCALE;
216+
const maxRadius = surfaceDiameter + numbers.PADDING;
217+
const fgScale = maxRadius / initialSize;
214218
const xfDuration = 1000 * Math.sqrt(maxRadius / 1024);
215219

216-
const scaleVal = baseElapsedTime / xfDuration;
220+
const scaleVal = baseElapsedTime / xfDuration * fgScale;
217221
t.doesNotThrow(() => td.verify(adapter.updateCssVariable(strings.VAR_FG_APPROX_XF, `scale(${scaleVal})`)));
218222
t.doesNotThrow(
219223
() => td.verify(

0 commit comments

Comments
 (0)