| OLD | NEW | 
 | (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> |  | 
| OLD | NEW |