OLD | NEW |
1 <!-- | 1 <!-- |
2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | 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 | 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 | 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 | 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 | 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 | 7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt |
8 --> | 8 --> |
9 | 9 |
10 <link rel="import" href="../polymer/polymer.html"> | 10 <link rel="import" href="../polymer/polymer.html"> |
11 <link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html
"> | 11 <link rel="import" href="../iron-a11y-keys-behavior/iron-a11y-keys-behavior.html
"> |
12 | 12 |
13 <!-- | 13 <!-- |
| 14 Material design: [Surface reaction](https://www.google.com/design/spec/animation
/responsive-interaction.html#responsive-interaction-surface-reaction) |
| 15 |
14 `paper-ripple` provides a visual effect that other paper elements can | 16 `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 | 17 use to simulate a rippling effect emanating from the point of contact. The |
16 effect can be visualized as a concentric circle with motion. | 18 effect can be visualized as a concentric circle with motion. |
17 | 19 |
18 Example: | 20 Example: |
19 | 21 |
20 <paper-ripple></paper-ripple> | 22 <div style="position:relative"> |
| 23 <paper-ripple></paper-ripple> |
| 24 </div> |
| 25 |
| 26 Note, it's important that the parent container of the ripple be relative positio
n, otherwise |
| 27 the ripple will emanate outside of the desired container. |
21 | 28 |
22 `paper-ripple` listens to "mousedown" and "mouseup" events so it would display r
ipple | 29 `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 | 30 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 | 31 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 | 32 important if you call `downAction()` you will have to make sure to call |
26 upAction() so that `paper-ripple` would end the animation loop. | 33 `upAction()` so that `paper-ripple` would end the animation loop. |
27 | 34 |
28 Example: | 35 Example: |
29 | 36 |
30 <paper-ripple id="ripple" style="pointer-events: none;"></paper-ripple> | 37 <paper-ripple id="ripple" style="pointer-events: none;"></paper-ripple> |
31 ... | 38 ... |
32 downAction: function(e) { | 39 downAction: function(e) { |
33 this.$.ripple.downAction({x: e.x, y: e.y}); | 40 this.$.ripple.downAction({x: e.x, y: e.y}); |
34 }, | 41 }, |
35 upAction: function(e) { | 42 upAction: function(e) { |
36 this.$.ripple.upAction(); | 43 this.$.ripple.upAction(); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
70 | 77 |
71 <!-- | 78 <!-- |
72 Fired when the animation finishes. This is useful if you want to wait until th
e ripple | 79 Fired when the animation finishes. This is useful if you want to wait until th
e ripple |
73 animation finishes to perform some action. | 80 animation finishes to perform some action. |
74 | 81 |
75 @event transitionend | 82 @event transitionend |
76 @param {Object} detail | 83 @param {Object} detail |
77 @param {Object} detail.node The animated node | 84 @param {Object} detail.node The animated node |
78 --> | 85 --> |
79 | 86 |
80 <style> | 87 <template> |
81 :host { | 88 <style> |
82 display: block; | 89 :host { |
83 position: absolute; | 90 display: block; |
84 border-radius: inherit; | 91 position: absolute; |
85 overflow: hidden; | 92 border-radius: inherit; |
86 top: 0; | 93 overflow: hidden; |
87 left: 0; | 94 top: 0; |
88 right: 0; | 95 left: 0; |
89 bottom: 0; | 96 right: 0; |
90 } | 97 bottom: 0; |
91 | 98 |
92 :host([animating]) { | 99 /* See PolymerElements/paper-behaviors/issues/34. On non-Chrome browsers
, |
93 /* This resolves a rendering issue in Chrome (as of 40) where the | 100 * creating a node (with a position:absolute) in the middle of an event |
94 ripple is not properly clipped by its parent (which may have | 101 * handler "interrupts" that event handler (which happens when the |
95 rounded corners). See: http://jsbin.com/temexa/4 | 102 * ripple is created on demand) */ |
| 103 pointer-events: none; |
| 104 } |
96 | 105 |
97 Note: We only apply this style conditionally. Otherwise, the browser | 106 :host([animating]) { |
98 will create a new compositing layer for every ripple element on the | 107 /* This resolves a rendering issue in Chrome (as of 40) where the |
99 page, and that would be bad. */ | 108 ripple is not properly clipped by its parent (which may have |
100 -webkit-transform: translate(0, 0); | 109 rounded corners). See: http://jsbin.com/temexa/4 |
101 transform: translate3d(0, 0, 0); | |
102 } | |
103 | 110 |
104 :host([noink]) { | 111 Note: We only apply this style conditionally. Otherwise, the browser |
105 pointer-events: none; | 112 will create a new compositing layer for every ripple element on the |
106 } | 113 page, and that would be bad. */ |
| 114 -webkit-transform: translate(0, 0); |
| 115 transform: translate3d(0, 0, 0); |
| 116 } |
107 | 117 |
108 #background, | 118 #background, |
109 #waves, | 119 #waves, |
110 .wave-container, | 120 .wave-container, |
111 .wave { | 121 .wave { |
112 pointer-events: none; | 122 pointer-events: none; |
113 position: absolute; | 123 position: absolute; |
114 top: 0; | 124 top: 0; |
115 left: 0; | 125 left: 0; |
116 width: 100%; | 126 width: 100%; |
117 height: 100%; | 127 height: 100%; |
118 } | 128 } |
119 | 129 |
120 #background, | 130 #background, |
121 .wave { | 131 .wave { |
122 opacity: 0; | 132 opacity: 0; |
123 } | 133 } |
124 | 134 |
125 #waves, | 135 #waves, |
126 .wave { | 136 .wave { |
127 overflow: hidden; | 137 overflow: hidden; |
128 } | 138 } |
129 | 139 |
130 .wave-container, | 140 .wave-container, |
131 .wave { | 141 .wave { |
132 border-radius: 50%; | 142 border-radius: 50%; |
133 } | 143 } |
134 | 144 |
135 :host(.circle) #background, | 145 :host(.circle) #background, |
136 :host(.circle) #waves { | 146 :host(.circle) #waves { |
137 border-radius: 50%; | 147 border-radius: 50%; |
138 } | 148 } |
139 | 149 |
140 :host(.circle) .wave-container { | 150 :host(.circle) .wave-container { |
141 overflow: hidden; | 151 overflow: hidden; |
142 } | 152 } |
| 153 </style> |
143 | 154 |
144 </style> | |
145 <template> | |
146 <div id="background"></div> | 155 <div id="background"></div> |
147 <div id="waves"></div> | 156 <div id="waves"></div> |
148 </template> | 157 </template> |
149 </dom-module> | 158 </dom-module> |
150 <script> | 159 <script> |
151 (function() { | 160 (function() { |
152 var Utility = { | 161 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) { | 162 distance: function(x1, y1, x2, y2) { |
168 var xDelta = (x1 - x2); | 163 var xDelta = (x1 - x2); |
169 var yDelta = (y1 - y2); | 164 var yDelta = (y1 - y2); |
170 | 165 |
171 return Math.sqrt(xDelta * xDelta + yDelta * yDelta); | 166 return Math.sqrt(xDelta * xDelta + yDelta * yDelta); |
172 }, | 167 }, |
173 | 168 |
174 now: (function() { | 169 now: window.performance && window.performance.now ? |
175 if (window.performance && window.performance.now) { | 170 window.performance.now.bind(window.performance) : Date.now |
176 return window.performance.now.bind(window.performance); | |
177 } | |
178 | |
179 return Date.now; | |
180 })() | |
181 }; | 171 }; |
182 | 172 |
183 /** | 173 /** |
184 * @param {HTMLElement} element | 174 * @param {HTMLElement} element |
185 * @constructor | 175 * @constructor |
186 */ | 176 */ |
187 function ElementMetrics(element) { | 177 function ElementMetrics(element) { |
188 this.element = element; | 178 this.element = element; |
189 this.width = this.boundingRect.width; | 179 this.width = this.boundingRect.width; |
190 this.height = this.boundingRect.height; | 180 this.height = this.boundingRect.height; |
(...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
538 /** | 528 /** |
539 * If true, the ripple will remain in the "down" state until `holdDown` | 529 * If true, the ripple will remain in the "down" state until `holdDown` |
540 * is set to false again. | 530 * is set to false again. |
541 */ | 531 */ |
542 holdDown: { | 532 holdDown: { |
543 type: Boolean, | 533 type: Boolean, |
544 value: false, | 534 value: false, |
545 observer: '_holdDownChanged' | 535 observer: '_holdDownChanged' |
546 }, | 536 }, |
547 | 537 |
| 538 /** |
| 539 * If true, the ripple will not generate a ripple effect |
| 540 * via pointer interaction. |
| 541 * Calling ripple's imperative api like `simulatedRipple` will |
| 542 * still generate the ripple effect. |
| 543 */ |
| 544 noink: { |
| 545 type: Boolean, |
| 546 value: false |
| 547 }, |
| 548 |
548 _animating: { | 549 _animating: { |
549 type: Boolean | 550 type: Boolean |
550 }, | 551 }, |
551 | 552 |
552 _boundAnimate: { | 553 _boundAnimate: { |
553 type: Function, | 554 type: Function, |
554 value: function() { | 555 value: function() { |
555 return this.animate.bind(this); | 556 return this.animate.bind(this); |
556 } | 557 } |
557 } | 558 } |
(...skipping 12 matching lines...) Expand all Loading... |
570 return target; | 571 return target; |
571 }, | 572 }, |
572 | 573 |
573 keyBindings: { | 574 keyBindings: { |
574 'enter:keydown': '_onEnterKeydown', | 575 'enter:keydown': '_onEnterKeydown', |
575 'space:keydown': '_onSpaceKeydown', | 576 'space:keydown': '_onSpaceKeydown', |
576 'space:keyup': '_onSpaceKeyup' | 577 'space:keyup': '_onSpaceKeyup' |
577 }, | 578 }, |
578 | 579 |
579 attached: function() { | 580 attached: function() { |
580 this.listen(this.target, 'up', 'upAction'); | 581 // Set up a11yKeysBehavior to listen to key events on the target, |
581 this.listen(this.target, 'down', 'downAction'); | 582 // so that space and enter activate the ripple even if the target doesn'
t |
| 583 // handle key events. The key handlers deal with `noink` themselves. |
| 584 this.keyEventTarget = this.target; |
| 585 this.listen(this.target, 'up', 'uiUpAction'); |
| 586 this.listen(this.target, 'down', 'uiDownAction'); |
| 587 }, |
582 | 588 |
583 if (!this.target.hasAttribute('noink')) { | 589 detached: function() { |
584 this.keyEventTarget = this.target; | 590 this.unlisten(this.target, 'up', 'uiUpAction'); |
585 } | 591 this.unlisten(this.target, 'down', 'uiDownAction'); |
586 }, | 592 }, |
587 | 593 |
588 get shouldKeepAnimating () { | 594 get shouldKeepAnimating () { |
589 for (var index = 0; index < this.ripples.length; ++index) { | 595 for (var index = 0; index < this.ripples.length; ++index) { |
590 if (!this.ripples[index].isAnimationComplete) { | 596 if (!this.ripples[index].isAnimationComplete) { |
591 return true; | 597 return true; |
592 } | 598 } |
593 } | 599 } |
594 | 600 |
595 return false; | 601 return false; |
596 }, | 602 }, |
597 | 603 |
598 simulatedRipple: function() { | 604 simulatedRipple: function() { |
599 this.downAction(null); | 605 this.downAction(null); |
600 | 606 |
601 // Please see polymer/polymer#1305 | 607 // Please see polymer/polymer#1305 |
602 this.async(function() { | 608 this.async(function() { |
603 this.upAction(); | 609 this.upAction(); |
604 }, 1); | 610 }, 1); |
605 }, | 611 }, |
606 | 612 |
607 /** @param {Event=} event */ | 613 /** |
| 614 * Provokes a ripple down effect via a UI event, |
| 615 * respecting the `noink` property. |
| 616 * @param {Event=} event |
| 617 */ |
| 618 uiDownAction: function(event) { |
| 619 if (!this.noink) { |
| 620 this.downAction(event); |
| 621 } |
| 622 }, |
| 623 |
| 624 /** |
| 625 * Provokes a ripple down effect via a UI event, |
| 626 * *not* respecting the `noink` property. |
| 627 * @param {Event=} event |
| 628 */ |
608 downAction: function(event) { | 629 downAction: function(event) { |
609 if (this.holdDown && this.ripples.length > 0) { | 630 if (this.holdDown && this.ripples.length > 0) { |
610 return; | 631 return; |
611 } | 632 } |
612 | 633 |
613 var ripple = this.addRipple(); | 634 var ripple = this.addRipple(); |
614 | 635 |
615 ripple.downAction(event); | 636 ripple.downAction(event); |
616 | 637 |
617 if (!this._animating) { | 638 if (!this._animating) { |
618 this.animate(); | 639 this.animate(); |
619 } | 640 } |
620 }, | 641 }, |
621 | 642 |
622 /** @param {Event=} event */ | 643 /** |
| 644 * Provokes a ripple up effect via a UI event, |
| 645 * respecting the `noink` property. |
| 646 * @param {Event=} event |
| 647 */ |
| 648 uiUpAction: function(event) { |
| 649 if (!this.noink) { |
| 650 this.upAction(event); |
| 651 } |
| 652 }, |
| 653 |
| 654 /** |
| 655 * Provokes a ripple up effect via a UI event, |
| 656 * *not* respecting the `noink` property. |
| 657 * @param {Event=} event |
| 658 */ |
623 upAction: function(event) { | 659 upAction: function(event) { |
624 if (this.holdDown) { | 660 if (this.holdDown) { |
625 return; | 661 return; |
626 } | 662 } |
627 | 663 |
628 this.ripples.forEach(function(ripple) { | 664 this.ripples.forEach(function(ripple) { |
629 ripple.upAction(event); | 665 ripple.upAction(event); |
630 }); | 666 }); |
631 | 667 |
632 this.animate(); | 668 this.animate(); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
685 } | 721 } |
686 | 722 |
687 if (!this.shouldKeepAnimating && this.ripples.length === 0) { | 723 if (!this.shouldKeepAnimating && this.ripples.length === 0) { |
688 this.onAnimationComplete(); | 724 this.onAnimationComplete(); |
689 } else { | 725 } else { |
690 window.requestAnimationFrame(this._boundAnimate); | 726 window.requestAnimationFrame(this._boundAnimate); |
691 } | 727 } |
692 }, | 728 }, |
693 | 729 |
694 _onEnterKeydown: function() { | 730 _onEnterKeydown: function() { |
695 this.downAction(); | 731 this.uiDownAction(); |
696 this.async(this.upAction, 1); | 732 this.async(this.uiUpAction, 1); |
697 }, | 733 }, |
698 | 734 |
699 _onSpaceKeydown: function() { | 735 _onSpaceKeydown: function() { |
700 this.downAction(); | 736 this.uiDownAction(); |
701 }, | 737 }, |
702 | 738 |
703 _onSpaceKeyup: function() { | 739 _onSpaceKeyup: function() { |
704 this.upAction(); | 740 this.uiUpAction(); |
705 }, | 741 }, |
706 | 742 |
707 _holdDownChanged: function(holdDown) { | 743 // note: holdDown does not respect noink since it can be a focus based |
708 if (holdDown) { | 744 // effect. |
| 745 _holdDownChanged: function(newVal, oldVal) { |
| 746 if (oldVal === undefined) { |
| 747 return; |
| 748 } |
| 749 if (newVal) { |
709 this.downAction(); | 750 this.downAction(); |
710 } else { | 751 } else { |
711 this.upAction(); | 752 this.upAction(); |
712 } | 753 } |
713 } | 754 } |
714 }); | 755 }); |
715 })(); | 756 })(); |
716 </script> | 757 </script> |
OLD | NEW |