Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Side by Side Diff: third_party/polymer/v0_8/components/paper-ripple/paper-ripple.html

Issue 1082403004: Import Polymer 0.8 and several key elements. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Also remove polymer/explainer Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 <!--
2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
3 This code may only be used under the BSD style license found at http://polymer.g ithub.io/LICENSE.txt
4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
5 The complete set of contributors may be found at http://polymer.github.io/CONTRI BUTORS.txt
6 Code distributed by Google as part of the polymer project is also
7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN TS.txt
8 -->
9
10 <!--
11 `paper-ripple` provides a visual effect that other paper elements can
12 use to simulate a rippling effect emanating from the point of contact. The
13 effect can be visualized as a concentric circle with motion.
14
15 Example:
16
17 <paper-ripple></paper-ripple>
18
19 `paper-ripple` listens to "down" and "up" events so it would display ripple
20 effect when touches on it. You can also defeat the default behavior and
21 manually route the down and up actions to the ripple element. Note that it is
22 important if you call downAction() you will have to make sure to call upAction()
23 so that `paper-ripple` would end the animation loop.
24
25 Example:
26
27 <paper-ripple id="ripple" style="pointer-events: none;"></paper-ripple>
28 ...
29 downAction: function(e) {
30 this.$.ripple.downAction({x: e.x, y: e.y});
31 },
32 upAction: function(e) {
33 this.$.ripple.upAction();
34 }
35
36 Styling ripple effect:
37
38 Use CSS color property to style the ripple:
39
40 paper-ripple {
41 color: #4285f4;
42 }
43
44 Note that CSS color property is inherited so it is not required to set it on
45 the `paper-ripple` element directly.
46
47 By default, the ripple is centered on the point of contact. Apply the `recenter s`
48 attribute to have the ripple grow toward the center of its container.
49
50 <paper-ripple recenters></paper-ripple>
51
52 Apply `circle` class to make the rippling effect within a circle.
53
54 <paper-ripple class="circle"></paper-ripple>
55
56 @group Paper Elements
57 @element paper-ripple
58 @homepage github.io
59 -->
60
61 <!--
62 Fired when the animation finishes. This is useful if you want to wait until the ripple
63 animation finishes to perform some action.
64
65 @event transitionend
66 @param {Object} detail
67 @param {Object} detail.node The animated node
68 -->
69
70 <link rel="import" href="../polymer/polymer.html">
71
72 <dom-module id="paper-ripple">
73 <style>
74 :host {
75 display: block;
76 position: absolute;
77 border-radius: inherit;
78 overflow: hidden;
79 top: 0;
80 left: 0;
81 right: 0;
82 bottom: 0;
83
84 /* This resolves a rendering issue in Chrome 40 where the
85 ripple is not properly clipped by its parent (which may have
86 rounded corners. See: http://jsbin.com/temexa/4 */
87 -webkit-transform: translate3d(0, 0, 0);
88 transform: translate3d(0, 0, 0);
89 }
90
91 :host([noink]) {
92 pointer-events: none;
93 }
94
95 #background,
96 #waves,
97 .wave-container,
98 .wave {
99 pointer-events: none;
100 position: absolute;
101 top: 0;
102 left: 0;
103 width: 100%;
104 height: 100%;
105 }
106
107 #background,
108 .wave {
109 opacity: 0;
110 }
111
112 #waves,
113 .wave {
114 overflow: hidden;
115 }
116
117 .wave-container,
118 .wave {
119 border-radius: 50%;
120 }
121
122 :host(.circle) #background,
123 :host(.circle) #waves {
124 border-radius: 50%;
125 }
126
127 :host(.circle) .wave-container {
128 overflow: hidden;
129 }
130
131 </style>
132 <template>
133 <div id="background"></div>
134 <div id="waves"></div>
135 </template>
136 </dom-module>
137 <script>
138 (function() {
139 var Utility = {
140 cssColorWithAlpha: function(cssColor, alpha) {
141 var parts = cssColor.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/);
142
143 if (typeof alpha == 'undefined') {
144 alpha = 1;
145 }
146
147 if (!parts) {
148 return 'rgba(255, 255, 255, ' + alpha + ')';
149 }
150
151 return 'rgba(' + parts[1] + ', ' + parts[2] + ', ' + parts[3] + ', ' + a lpha + ')';
152 },
153
154 distance: function(x1, y1, x2, y2) {
155 var xDelta = (x1 - x2);
156 var yDelta = (y1 - y2);
157
158 return Math.sqrt(xDelta * xDelta + yDelta * yDelta);
159 },
160
161 now: (function() {
162 if (window.performance && window.performance.now) {
163 return window.performance.now.bind(window.performance);
164 }
165
166 return Date.now;
167 })()
168 };
169
170 /**
171 * @param {HTMLElement} element
172 * @constructor
173 */
174 function ElementMetrics(element) {
175 this.element = element;
176 this.width = this.boundingRect.width;
177 this.height = this.boundingRect.height;
178
179 this.size = Math.max(this.width, this.height);
180 }
181
182 ElementMetrics.prototype = {
183 get boundingRect () {
184 return this.element.getBoundingClientRect();
185 },
186
187 furthestCornerDistanceFrom: function(x, y) {
188 var topLeft = Utility.distance(x, y, 0, 0);
189 var topRight = Utility.distance(x, y, this.width, 0);
190 var bottomLeft = Utility.distance(x, y, 0, this.height);
191 var bottomRight = Utility.distance(x, y, this.width, this.height);
192
193 return Math.max(topLeft, topRight, bottomLeft, bottomRight);
194 }
195 };
196
197 /**
198 * @param {HTMLElement} element
199 * @constructor
200 */
201 function Ripple(element) {
202 this.element = element;
203 this.color = window.getComputedStyle(element).color;
204
205 this.wave = document.createElement('div');
206 this.waveContainer = document.createElement('div');
207 this.wave.style.backgroundColor = this.color;
208 this.wave.classList.add('wave');
209 this.waveContainer.classList.add('wave-container');
210 Polymer.dom(this.waveContainer).appendChild(this.wave);
211
212 this.resetInteractionState();
213 }
214
215 Ripple.MAX_RADIUS = 300;
216
217 Ripple.prototype = {
218 get recenters() {
219 return this.element.recenters;
220 },
221
222 get mouseDownElapsed() {
223 var elapsed;
224
225 if (!this.mouseDownStart) {
226 return 0;
227 }
228
229 elapsed = Utility.now() - this.mouseDownStart;
230
231 if (this.mouseUpStart) {
232 elapsed -= this.mouseUpElapsed;
233 }
234
235 return elapsed;
236 },
237
238 get mouseUpElapsed() {
239 return this.mouseUpStart ?
240 Utility.now () - this.mouseUpStart : 0;
241 },
242
243 get mouseDownElapsedSeconds() {
244 return this.mouseDownElapsed / 1000;
245 },
246
247 get mouseUpElapsedSeconds() {
248 return this.mouseUpElapsed / 1000;
249 },
250
251 get mouseInteractionSeconds() {
252 return this.mouseDownElapsedSeconds + this.mouseUpElapsedSeconds;
253 },
254
255 get initialOpacity() {
256 return this.element.initialOpacity;
257 },
258
259 get opacityDecayVelocity() {
260 return this.element.opacityDecayVelocity;
261 },
262
263 get radius() {
264 var width2 = this.containerMetrics.width * this.containerMetrics.width;
265 var height2 = this.containerMetrics.height * this.containerMetrics.heigh t;
266 var waveRadius = Math.min(
267 Math.sqrt(width2 + height2),
268 Ripple.MAX_RADIUS
269 ) * 1.1 + 5;
270
271 var duration = 1.1 - 0.2 * (waveRadius / Ripple.MAX_RADIUS);
272 var timeNow = this.mouseInteractionSeconds / duration;
273 var size = waveRadius * (1 - Math.pow(80, -timeNow));
274
275 return Math.abs(size);
276 },
277
278 get opacity() {
279 if (!this.mouseUpStart) {
280 return this.initialOpacity;
281 }
282
283 return Math.max(
284 0,
285 this.initialOpacity - this.mouseUpElapsedSeconds * this.opacityDecayVe locity
286 );
287 },
288
289 get outerOpacity() {
290 // Linear increase in background opacity, capped at the opacity
291 // of the wavefront (waveOpacity).
292 var outerOpacity = this.mouseUpElapsedSeconds * 0.3;
293 var waveOpacity = this.opacity;
294
295 return Math.max(
296 0,
297 Math.min(outerOpacity, waveOpacity)
298 );
299 },
300
301 get isOpacityFullyDecayed() {
302 return this.opacity < 0.01 &&
303 this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS);
304 },
305
306 get isRestingAtMaxRadius() {
307 return this.opacity >= this.initialOpacity &&
308 this.radius >= Math.min(this.maxRadius, Ripple.MAX_RADIUS);
309 },
310
311 get isAnimationComplete() {
312 return this.mouseUpStart ?
313 this.isOpacityFullyDecayed : this.isRestingAtMaxRadius;
314 },
315
316 get translationFraction() {
317 return Math.min(
318 1,
319 this.radius / this.containerMetrics.size * 2 / Math.sqrt(2)
320 );
321 },
322
323 get xNow() {
324 if (this.xEnd) {
325 return this.xStart + this.translationFraction * (this.xEnd - this.xSta rt);
326 }
327
328 return this.xStart;
329 },
330
331 get yNow() {
332 if (this.yEnd) {
333 return this.yStart + this.translationFraction * (this.yEnd - this.ySta rt);
334 }
335
336 return this.yStart;
337 },
338
339 get isMouseDown() {
340 return this.mouseDownStart && !this.mouseUpStart;
341 },
342
343 resetInteractionState: function() {
344 this.maxRadius = 0;
345 this.mouseDownStart = 0;
346 this.mouseUpStart = 0;
347
348 this.xStart = 0;
349 this.yStart = 0;
350 this.xEnd = 0;
351 this.yEnd = 0;
352 this.slideDistance = 0;
353
354 this.containerMetrics = new ElementMetrics(this.element);
355 },
356
357 draw: function() {
358 var scale;
359 var translateString;
360 var dx;
361 var dy;
362
363 this.wave.style.opacity = this.opacity;
364
365 scale = this.radius / (this.containerMetrics.size / 2);
366 dx = this.xNow - (this.containerMetrics.width / 2);
367 dy = this.yNow - (this.containerMetrics.height / 2);
368
369 Polymer.Base.translate3d(this.waveContainer, dx + 'px', dy + 'px', 0);
370
371 // 2d transform for safari because of border-radius and overflow:hidden clipping bug.
372 // https://bugs.webkit.org/show_bug.cgi?id=98538
373 this.wave.style.webkitTransform = 'scale(' + scale + ',' + scale + ')';
374 this.wave.style.transform = 'scale3d(' + scale + ',' + scale + ',1)';
375 },
376
377 mousedownAction: function(event) {
378 this.resetInteractionState();
379 this.mouseDownStart = Utility.now();
380
381 this.xStart = event ?
382 event.x - this.containerMetrics.boundingRect.left :
383 this.containerMetrics.width / 2;
384 this.yStart = event ?
385 event.y - this.containerMetrics.boundingRect.top :
386 this.containerMetrics.height / 2;
387
388 if (this.recenters) {
389 this.xEnd = this.containerMetrics.width / 2;
390 this.yEnd = this.containerMetrics.height / 2;
391 this.slideDistance = Utility.distance(
392 this.xStart, this.yStart, this.xEnd, this.yEnd
393 );
394 }
395
396 this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom(
397 this.xStart,
398 this.yStart
399 );
400
401 this.waveContainer.style.top =
402 (this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px' ;
403 this.waveContainer.style.left =
404 (this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px';
405
406 this.waveContainer.style.width = this.containerMetrics.size + 'px';
407 this.waveContainer.style.height = this.containerMetrics.size + 'px';
408 },
409
410 mouseupAction: function(event) {
411 if (!this.isMouseDown) {
412 return;
413 }
414
415 this.mouseUpStart = Utility.now();
416 },
417
418 remove: function() {
419 Polymer.dom(this.waveContainer.parentNode).removeChild(
420 this.waveContainer
421 );
422 }
423 };
424
425 Polymer({
426 is: 'paper-ripple',
427
428 properties: {
429 /**
430 * The initial opacity set on the wave.
431 *
432 * @attribute initialOpacity
433 * @type number
434 * @default 0.25
435 */
436 initialOpacity: {
437 type: Number,
438 value: 0.25
439 },
440
441 /**
442 * How fast (opacity per second) the wave fades out.
443 *
444 * @attribute opacityDecayVelocity
445 * @type number
446 * @default 0.8
447 */
448 opacityDecayVelocity: {
449 type: Number,
450 value: 0.8
451 },
452
453 /**
454 * If true, ripples will exhibit a gravitational pull towards
455 * the center of their container as they fade away.
456 *
457 * @attribute recenters
458 * @type boolean
459 * @default false
460 */
461 recenters: {
462 type: Boolean,
463 value: false
464 },
465
466 /**
467 * A list of the visual ripples.
468 *
469 * @attribute ripples
470 * @type Array
471 * @default []
472 */
473 ripples: {
474 type: Array,
475 value: function() {
476 return [];
477 }
478 },
479
480 _animating: {
481 type: Boolean
482 },
483
484 _boundAnimate: {
485 type: Function,
486 value: function() {
487 return this.animate.bind(this);
488 }
489 },
490
491 _boundMousedownAction: {
492 type: Function,
493 value: function() {
494 return this.mousedownAction.bind(this);
495 }
496 },
497
498 _boundMouseupAction: {
499 type: Function,
500 value: function() {
501 return this.mouseupAction.bind(this);
502 }
503 }
504 },
505
506 get target () {
507 return this.host || this.parentNode;
508 },
509
510 attached: function() {
511 this.target.addEventListener('mousedown', this._boundMousedownAction);
512 this.target.addEventListener('mouseup', this._boundMouseupAction);
513 },
514
515 detached: function() {
516 this.target.removeEventListener('mousedown', this._boundMousedownAction) ;
517 this.target.removeEventListener('mouseup', this._boundMouseupAction);
518 },
519
520 /* TODO(cdata): Replace the above attached / detached listeners when
521 PolymerGestures equivalent lands in 0.8.
522 listeners: {
523 mousedown: 'mousedownAction',
524 mouseup: 'mouseupAction'
525 },
526 */
527
528 get shouldKeepAnimating () {
529 for (var index = 0; index < this.ripples.length; ++index) {
530 if (!this.ripples[index].isAnimationComplete) {
531 return true;
532 }
533 }
534
535 return false;
536 },
537
538 simulatedRipple: function() {
539 this.mousedownAction(null);
540
541 // Please see polymer/polymer#1305
542 this.async(function() {
543 this.mouseupAction();
544 }, 1);
545 },
546
547 mousedownAction: function(event) {
548 var ripple = this.addRipple();
549
550 ripple.mousedownAction(event);
551
552 if (!this._animating) {
553 this.animate();
554 }
555 },
556
557 mouseupAction: function(event) {
558 this.ripples.forEach(function(ripple) {
559 ripple.mouseupAction(event);
560 });
561
562 this.animate();
563 },
564
565 onAnimationComplete: function() {
566 this._animating = false;
567 this.$.background.style.backgroundColor = null;
568 this.fire('transitionend');
569 },
570
571 addRipple: function() {
572 var ripple = new Ripple(this);
573
574 Polymer.dom(this.$.waves).appendChild(ripple.waveContainer);
575 this.$.background.style.backgroundColor = ripple.color;
576 this.ripples.push(ripple);
577
578 return ripple;
579 },
580
581 removeRipple: function(ripple) {
582 var rippleIndex = this.ripples.indexOf(ripple);
583
584 if (rippleIndex < 0) {
585 return;
586 }
587
588 this.ripples.splice(rippleIndex, 1);
589
590 ripple.remove();
591 },
592
593 animate: function() {
594 var index;
595 var ripple;
596
597 this._animating = true;
598
599 for (index = 0; index < this.ripples.length; ++index) {
600 ripple = this.ripples[index];
601
602 ripple.draw();
603
604 this.$.background.style.opacity = ripple.outerOpacity;
605
606 if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) {
607 this.removeRipple(ripple);
608 }
609 }
610
611 if (this.shouldKeepAnimating) {
612 window.requestAnimationFrame(this._boundAnimate);
613 } else if (this.ripples.length === 0) {
614 this.onAnimationComplete();
615 }
616 }
617 });
618 })();
619 </script>
620
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698