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

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

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

Powered by Google App Engine
This is Rietveld 408576698