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 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
69 <dom-module id="paper-ripple"> | 76 <dom-module id="paper-ripple"> |
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 <template> | 87 <template> |
81 <style> | 88 <style> |
82 :host { | 89 :host { |
83 display: block; | 90 display: block; |
84 position: absolute; | 91 position: absolute; |
85 border-radius: inherit; | 92 border-radius: inherit; |
86 overflow: hidden; | 93 overflow: hidden; |
87 top: 0; | 94 top: 0; |
88 left: 0; | 95 left: 0; |
89 right: 0; | 96 right: 0; |
90 bottom: 0; | 97 bottom: 0; |
| 98 |
| 99 /* See PolymerElements/paper-behaviors/issues/34. On non-Chrome browsers
, |
| 100 * creating a node (with a position:absolute) in the middle of an event |
| 101 * handler "interrupts" that event handler (which happens when the |
| 102 * ripple is created on demand) */ |
| 103 pointer-events: none; |
91 } | 104 } |
92 | 105 |
93 :host([animating]) { | 106 :host([animating]) { |
94 /* This resolves a rendering issue in Chrome (as of 40) where the | 107 /* This resolves a rendering issue in Chrome (as of 40) where the |
95 ripple is not properly clipped by its parent (which may have | 108 ripple is not properly clipped by its parent (which may have |
96 rounded corners). See: http://jsbin.com/temexa/4 | 109 rounded corners). See: http://jsbin.com/temexa/4 |
97 | 110 |
98 Note: We only apply this style conditionally. Otherwise, the browser | 111 Note: We only apply this style conditionally. Otherwise, the browser |
99 will create a new compositing layer for every ripple element on the | 112 will create a new compositing layer for every ripple element on the |
100 page, and that would be bad. */ | 113 page, and that would be bad. */ |
101 -webkit-transform: translate(0, 0); | 114 -webkit-transform: translate(0, 0); |
102 transform: translate3d(0, 0, 0); | 115 transform: translate3d(0, 0, 0); |
103 } | 116 } |
104 | 117 |
105 :host([noink]) { | 118 :host([noink]) { |
106 pointer-events: none; | 119 pointer-events: none; |
107 } | 120 } |
108 | 121 |
109 #background, | 122 #background, |
110 #waves, | 123 #waves, |
111 .wave-container, | 124 .wave-container, |
112 .wave { | 125 .wave { |
113 pointer-events: none; | 126 pointer-events: none; |
114 position: absolute; | 127 position: absolute; |
115 top: 0; | 128 top: 0; |
116 left: 0; | 129 left: 0; |
117 width: 100%; | 130 width: 100%; |
118 height: 100%; | 131 height: 100%; |
119 } | 132 } |
120 | 133 |
121 #background, | 134 #background, |
122 .wave { | 135 .wave { |
123 opacity: 0; | 136 opacity: 0; |
124 } | 137 } |
125 | 138 |
126 #waves, | 139 #waves, |
127 .wave { | 140 .wave { |
128 overflow: hidden; | 141 overflow: hidden; |
129 } | 142 } |
130 | 143 |
131 .wave-container, | 144 .wave-container, |
132 .wave { | 145 .wave { |
133 border-radius: 50%; | 146 border-radius: 50%; |
134 } | 147 } |
135 | 148 |
136 :host(.circle) #background, | 149 :host(.circle) #background, |
137 :host(.circle) #waves { | 150 :host(.circle) #waves { |
138 border-radius: 50%; | 151 border-radius: 50%; |
139 } | 152 } |
140 | 153 |
141 :host(.circle) .wave-container { | 154 :host(.circle) .wave-container { |
142 overflow: hidden; | 155 overflow: hidden; |
143 } | 156 } |
144 </style> | 157 </style> |
145 | 158 |
146 <div id="background"></div> | 159 <div id="background"></div> |
147 <div id="waves"></div> | 160 <div id="waves"></div> |
148 </template> | 161 </template> |
149 </dom-module> | 162 </dom-module> |
150 <script> | 163 <script> |
151 (function() { | 164 (function() { |
152 var Utility = { | 165 var Utility = { |
153 distance: function(x1, y1, x2, y2) { | 166 distance: function(x1, y1, x2, y2) { |
154 var xDelta = (x1 - x2); | 167 var xDelta = (x1 - x2); |
155 var yDelta = (y1 - y2); | 168 var yDelta = (y1 - y2); |
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
519 /** | 532 /** |
520 * If true, the ripple will remain in the "down" state until `holdDown` | 533 * If true, the ripple will remain in the "down" state until `holdDown` |
521 * is set to false again. | 534 * is set to false again. |
522 */ | 535 */ |
523 holdDown: { | 536 holdDown: { |
524 type: Boolean, | 537 type: Boolean, |
525 value: false, | 538 value: false, |
526 observer: '_holdDownChanged' | 539 observer: '_holdDownChanged' |
527 }, | 540 }, |
528 | 541 |
| 542 /** |
| 543 * If true, the ripple will not generate a ripple effect |
| 544 * via pointer interaction. |
| 545 * Calling ripple's imperative api like `simulatedRipple` will |
| 546 * still generate the ripple effect. |
| 547 */ |
| 548 noink: { |
| 549 type: Boolean, |
| 550 value: false |
| 551 }, |
| 552 |
529 _animating: { | 553 _animating: { |
530 type: Boolean | 554 type: Boolean |
531 }, | 555 }, |
532 | 556 |
533 _boundAnimate: { | 557 _boundAnimate: { |
534 type: Function, | 558 type: Function, |
535 value: function() { | 559 value: function() { |
536 return this.animate.bind(this); | 560 return this.animate.bind(this); |
537 } | 561 } |
538 } | 562 } |
539 }, | 563 }, |
540 | 564 |
| 565 observers: [ |
| 566 '_noinkChanged(noink, isAttached)' |
| 567 ], |
| 568 |
541 get target () { | 569 get target () { |
542 var ownerRoot = Polymer.dom(this).getOwnerRoot(); | 570 var ownerRoot = Polymer.dom(this).getOwnerRoot(); |
543 var target; | 571 var target; |
544 | 572 |
545 if (this.parentNode.nodeType == 11) { // DOCUMENT_FRAGMENT_NODE | 573 if (this.parentNode.nodeType == 11) { // DOCUMENT_FRAGMENT_NODE |
546 target = ownerRoot.host; | 574 target = ownerRoot.host; |
547 } else { | 575 } else { |
548 target = this.parentNode; | 576 target = this.parentNode; |
549 } | 577 } |
550 | 578 |
551 return target; | 579 return target; |
552 }, | 580 }, |
553 | 581 |
554 keyBindings: { | 582 keyBindings: { |
555 'enter:keydown': '_onEnterKeydown', | 583 'enter:keydown': '_onEnterKeydown', |
556 'space:keydown': '_onSpaceKeydown', | 584 'space:keydown': '_onSpaceKeydown', |
557 'space:keyup': '_onSpaceKeyup' | 585 'space:keyup': '_onSpaceKeyup' |
558 }, | 586 }, |
559 | 587 |
560 attached: function() { | 588 attached: function() { |
561 this.listen(this.target, 'up', 'upAction'); | 589 this.listen(this.target, 'up', 'uiUpAction'); |
562 this.listen(this.target, 'down', 'downAction'); | 590 this.listen(this.target, 'down', 'uiDownAction'); |
| 591 }, |
563 | 592 |
564 if (!this.target.hasAttribute('noink')) { | 593 detached: function() { |
565 this.keyEventTarget = this.target; | 594 this.unlisten(this.target, 'up', 'uiUpAction'); |
566 } | 595 this.unlisten(this.target, 'down', 'uiDownAction'); |
567 }, | 596 }, |
568 | 597 |
569 get shouldKeepAnimating () { | 598 get shouldKeepAnimating () { |
570 for (var index = 0; index < this.ripples.length; ++index) { | 599 for (var index = 0; index < this.ripples.length; ++index) { |
571 if (!this.ripples[index].isAnimationComplete) { | 600 if (!this.ripples[index].isAnimationComplete) { |
572 return true; | 601 return true; |
573 } | 602 } |
574 } | 603 } |
575 | 604 |
576 return false; | 605 return false; |
577 }, | 606 }, |
578 | 607 |
579 simulatedRipple: function() { | 608 simulatedRipple: function() { |
580 this.downAction(null); | 609 this.downAction(null); |
581 | 610 |
582 // Please see polymer/polymer#1305 | 611 // Please see polymer/polymer#1305 |
583 this.async(function() { | 612 this.async(function() { |
584 this.upAction(); | 613 this.upAction(); |
585 }, 1); | 614 }, 1); |
586 }, | 615 }, |
587 | 616 |
588 /** @param {Event=} event */ | 617 /** |
| 618 * Provokes a ripple down effect via a UI event, |
| 619 * respecting the `noink` property. |
| 620 * @param {Event=} event |
| 621 */ |
| 622 uiDownAction: function(event) { |
| 623 if (!this.noink) { |
| 624 this.downAction(event); |
| 625 } |
| 626 }, |
| 627 |
| 628 /** |
| 629 * Provokes a ripple down effect via a UI event, |
| 630 * *not* respecting the `noink` property. |
| 631 * @param {Event=} event |
| 632 */ |
589 downAction: function(event) { | 633 downAction: function(event) { |
590 if (this.holdDown && this.ripples.length > 0) { | 634 if (this.holdDown && this.ripples.length > 0) { |
591 return; | 635 return; |
592 } | 636 } |
593 | 637 |
594 var ripple = this.addRipple(); | 638 var ripple = this.addRipple(); |
595 | 639 |
596 ripple.downAction(event); | 640 ripple.downAction(event); |
597 | 641 |
598 if (!this._animating) { | 642 if (!this._animating) { |
599 this.animate(); | 643 this.animate(); |
600 } | 644 } |
601 }, | 645 }, |
602 | 646 |
603 /** @param {Event=} event */ | 647 /** |
| 648 * Provokes a ripple up effect via a UI event, |
| 649 * respecting the `noink` property. |
| 650 * @param {Event=} event |
| 651 */ |
| 652 uiUpAction: function(event) { |
| 653 if (!this.noink) { |
| 654 this.upAction(event); |
| 655 } |
| 656 }, |
| 657 |
| 658 /** |
| 659 * Provokes a ripple up effect via a UI event, |
| 660 * *not* respecting the `noink` property. |
| 661 * @param {Event=} event |
| 662 */ |
604 upAction: function(event) { | 663 upAction: function(event) { |
605 if (this.holdDown) { | 664 if (this.holdDown) { |
606 return; | 665 return; |
607 } | 666 } |
608 | 667 |
609 this.ripples.forEach(function(ripple) { | 668 this.ripples.forEach(function(ripple) { |
610 ripple.upAction(event); | 669 ripple.upAction(event); |
611 }); | 670 }); |
612 | 671 |
613 this.animate(); | 672 this.animate(); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
666 } | 725 } |
667 | 726 |
668 if (!this.shouldKeepAnimating && this.ripples.length === 0) { | 727 if (!this.shouldKeepAnimating && this.ripples.length === 0) { |
669 this.onAnimationComplete(); | 728 this.onAnimationComplete(); |
670 } else { | 729 } else { |
671 window.requestAnimationFrame(this._boundAnimate); | 730 window.requestAnimationFrame(this._boundAnimate); |
672 } | 731 } |
673 }, | 732 }, |
674 | 733 |
675 _onEnterKeydown: function() { | 734 _onEnterKeydown: function() { |
676 this.downAction(); | 735 this.uiDownAction(); |
677 this.async(this.upAction, 1); | 736 this.async(this.uiUpAction, 1); |
678 }, | 737 }, |
679 | 738 |
680 _onSpaceKeydown: function() { | 739 _onSpaceKeydown: function() { |
681 this.downAction(); | 740 this.uiDownAction(); |
682 }, | 741 }, |
683 | 742 |
684 _onSpaceKeyup: function() { | 743 _onSpaceKeyup: function() { |
685 this.upAction(); | 744 this.uiUpAction(); |
686 }, | 745 }, |
687 | 746 |
688 _holdDownChanged: function(holdDown) { | 747 // note: holdDown does not respect noink since it can be a focus based |
689 if (holdDown) { | 748 // effect. |
| 749 _holdDownChanged: function(newVal, oldVal) { |
| 750 if (oldVal === undefined) { |
| 751 return; |
| 752 } |
| 753 if (newVal) { |
690 this.downAction(); | 754 this.downAction(); |
691 } else { | 755 } else { |
692 this.upAction(); | 756 this.upAction(); |
693 } | 757 } |
| 758 }, |
| 759 |
| 760 _noinkChanged: function(noink, attached) { |
| 761 if (attached) { |
| 762 this.keyEventTarget = noink ? this : this.target; |
| 763 } |
694 } | 764 } |
695 }); | 765 }); |
696 })(); | 766 })(); |
697 </script> | 767 </script> |
OLD | NEW |