| 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 |