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

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

Issue 1269803005: Remove third_party/polymer from .gitignore (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 4 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 /** @param {Event=} event */
396 downAction: function(event) {
397 var xCenter = this.containerMetrics.width / 2;
398 var yCenter = this.containerMetrics.height / 2;
399
400 this.resetInteractionState();
401 this.mouseDownStart = Utility.now();
402
403 if (this.center) {
404 this.xStart = xCenter;
405 this.yStart = yCenter;
406 this.slideDistance = Utility.distance(
407 this.xStart, this.yStart, this.xEnd, this.yEnd
408 );
409 } else {
410 this.xStart = event ?
411 event.detail.x - this.containerMetrics.boundingRect.left :
412 this.containerMetrics.width / 2;
413 this.yStart = event ?
414 event.detail.y - this.containerMetrics.boundingRect.top :
415 this.containerMetrics.height / 2;
416 }
417
418 if (this.recenters) {
419 this.xEnd = xCenter;
420 this.yEnd = yCenter;
421 this.slideDistance = Utility.distance(
422 this.xStart, this.yStart, this.xEnd, this.yEnd
423 );
424 }
425
426 this.maxRadius = this.containerMetrics.furthestCornerDistanceFrom(
427 this.xStart,
428 this.yStart
429 );
430
431 this.waveContainer.style.top =
432 (this.containerMetrics.height - this.containerMetrics.size) / 2 + 'px' ;
433 this.waveContainer.style.left =
434 (this.containerMetrics.width - this.containerMetrics.size) / 2 + 'px';
435
436 this.waveContainer.style.width = this.containerMetrics.size + 'px';
437 this.waveContainer.style.height = this.containerMetrics.size + 'px';
438 },
439
440 /** @param {Event=} event */
441 upAction: function(event) {
442 if (!this.isMouseDown) {
443 return;
444 }
445
446 this.mouseUpStart = Utility.now();
447 },
448
449 remove: function() {
450 Polymer.dom(this.waveContainer.parentNode).removeChild(
451 this.waveContainer
452 );
453 }
454 };
455
456 Polymer({
457 is: 'paper-ripple',
458
459 behaviors: [
460 Polymer.IronA11yKeysBehavior
461 ],
462
463 properties: {
464 /**
465 * The initial opacity set on the wave.
466 *
467 * @attribute initialOpacity
468 * @type number
469 * @default 0.25
470 */
471 initialOpacity: {
472 type: Number,
473 value: 0.25
474 },
475
476 /**
477 * How fast (opacity per second) the wave fades out.
478 *
479 * @attribute opacityDecayVelocity
480 * @type number
481 * @default 0.8
482 */
483 opacityDecayVelocity: {
484 type: Number,
485 value: 0.8
486 },
487
488 /**
489 * If true, ripples will exhibit a gravitational pull towards
490 * the center of their container as they fade away.
491 *
492 * @attribute recenters
493 * @type boolean
494 * @default false
495 */
496 recenters: {
497 type: Boolean,
498 value: false
499 },
500
501 /**
502 * If true, ripples will center inside its container
503 *
504 * @attribute recenters
505 * @type boolean
506 * @default false
507 */
508 center: {
509 type: Boolean,
510 value: false
511 },
512
513 /**
514 * A list of the visual ripples.
515 *
516 * @attribute ripples
517 * @type Array
518 * @default []
519 */
520 ripples: {
521 type: Array,
522 value: function() {
523 return [];
524 }
525 },
526
527 /**
528 * True when there are visible ripples animating within the
529 * element.
530 */
531 animating: {
532 type: Boolean,
533 readOnly: true,
534 reflectToAttribute: true,
535 value: false
536 },
537
538 /**
539 * If true, the ripple will remain in the "down" state until `holdDown`
540 * is set to false again.
541 */
542 holdDown: {
543 type: Boolean,
544 value: false,
545 observer: '_holdDownChanged'
546 },
547
548 _animating: {
549 type: Boolean
550 },
551
552 _boundAnimate: {
553 type: Function,
554 value: function() {
555 return this.animate.bind(this);
556 }
557 }
558 },
559
560 get target () {
561 var ownerRoot = Polymer.dom(this).getOwnerRoot();
562 var target;
563
564 if (this.parentNode.nodeType == 11) { // DOCUMENT_FRAGMENT_NODE
565 target = ownerRoot.host;
566 } else {
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', 'upAction');
581 this.listen(this.target, 'down', 'downAction');
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 /** @param {Event=} event */
608 downAction: function(event) {
609 if (this.holdDown && this.ripples.length > 0) {
610 return;
611 }
612
613 var ripple = this.addRipple();
614
615 ripple.downAction(event);
616
617 if (!this._animating) {
618 this.animate();
619 }
620 },
621
622 /** @param {Event=} event */
623 upAction: function(event) {
624 if (this.holdDown) {
625 return;
626 }
627
628 this.ripples.forEach(function(ripple) {
629 ripple.upAction(event);
630 });
631
632 this.animate();
633 },
634
635 onAnimationComplete: function() {
636 this._animating = false;
637 this.$.background.style.backgroundColor = null;
638 this.fire('transitionend');
639 },
640
641 addRipple: function() {
642 var ripple = new Ripple(this);
643
644 Polymer.dom(this.$.waves).appendChild(ripple.waveContainer);
645 this.$.background.style.backgroundColor = ripple.color;
646 this.ripples.push(ripple);
647
648 this._setAnimating(true);
649
650 return ripple;
651 },
652
653 removeRipple: function(ripple) {
654 var rippleIndex = this.ripples.indexOf(ripple);
655
656 if (rippleIndex < 0) {
657 return;
658 }
659
660 this.ripples.splice(rippleIndex, 1);
661
662 ripple.remove();
663
664 if (!this.ripples.length) {
665 this._setAnimating(false);
666 }
667 },
668
669 animate: function() {
670 var index;
671 var ripple;
672
673 this._animating = true;
674
675 for (index = 0; index < this.ripples.length; ++index) {
676 ripple = this.ripples[index];
677
678 ripple.draw();
679
680 this.$.background.style.opacity = ripple.outerOpacity;
681
682 if (ripple.isOpacityFullyDecayed && !ripple.isRestingAtMaxRadius) {
683 this.removeRipple(ripple);
684 }
685 }
686
687 if (!this.shouldKeepAnimating && this.ripples.length === 0) {
688 this.onAnimationComplete();
689 } else {
690 window.requestAnimationFrame(this._boundAnimate);
691 }
692 },
693
694 _onEnterKeydown: function() {
695 this.downAction();
696 this.async(this.upAction, 1);
697 },
698
699 _onSpaceKeydown: function() {
700 this.downAction();
701 },
702
703 _onSpaceKeyup: function() {
704 this.upAction();
705 },
706
707 _holdDownChanged: function(holdDown) {
708 if (holdDown) {
709 this.downAction();
710 } else {
711 this.upAction();
712 }
713 }
714 });
715 })();
716 </script>
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698