1<!-- 2@license 3Copyright (c) 2015 The Polymer Project Authors. All rights reserved. 4This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7Code distributed by Google as part of the polymer project is also 8subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9--> 10<link rel="import" href="../polymer/polymer.html"> 11<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html"> 12<link rel="import" href="../iron-selector/iron-selectable.html"> 13<link rel="import" href="neon-animation-runner-behavior.html"> 14 15<!-- 16Material design: [Meaningful transitions](https://www.google.com/design/spec/animation/meaningful-transitions.html) 17 18`neon-animated-pages` manages a set of pages and runs an animation when switching between them. Its 19children pages should implement `Polymer.NeonAnimatableBehavior` and define `entry` and `exit` 20animations to be run when switching to or switching out of the page. 21 22@group Neon Elements 23@element neon-animated-pages 24@demo demo/index.html 25--> 26 27<dom-module id="neon-animated-pages"> 28 <template> 29 <style> 30 :host { 31 display: block; 32 position: relative; 33 } 34 35 :host > ::content > * { 36 position: absolute; 37 top: 0; 38 left: 0; 39 bottom: 0; 40 right: 0; 41 } 42 43 :host > ::content > :not(.iron-selected):not(.neon-animating) { 44 display: none !important; 45 } 46 47 :host > ::content > .neon-animating { 48 pointer-events: none; 49 } 50 </style> 51 52 <content id="content"></content> 53 </template> 54 55</dom-module> 56 57<script> 58(function() { 59 60 Polymer({ 61 62 is: 'neon-animated-pages', 63 64 behaviors: [ 65 Polymer.IronResizableBehavior, 66 Polymer.IronSelectableBehavior, 67 Polymer.NeonAnimationRunnerBehavior 68 ], 69 70 properties: { 71 72 activateEvent: { 73 type: String, 74 value: '' 75 }, 76 77 // if true, the initial page selection will also be animated according to its animation config. 78 animateInitialSelection: { 79 type: Boolean, 80 value: false 81 } 82 83 }, 84 85 listeners: { 86 'iron-select': '_onIronSelect', 87 'neon-animation-finish': '_onNeonAnimationFinish' 88 }, 89 90 _onIronSelect: function(event) { 91 var selectedPage = event.detail.item; 92 93 // Only consider child elements. 94 if (this.items.indexOf(selectedPage) < 0) { 95 return; 96 } 97 98 var oldPage = this._valueToItem(this._prevSelected) || false; 99 this._prevSelected = this.selected; 100 101 // on initial load and if animateInitialSelection is negated, simply display selectedPage. 102 if (!oldPage && !this.animateInitialSelection) { 103 this._completeSelectedChanged(); 104 return; 105 } 106 107 this.animationConfig = []; 108 109 // configure selectedPage animations. 110 if (this.entryAnimation) { 111 this.animationConfig.push({ 112 name: this.entryAnimation, 113 node: selectedPage 114 }); 115 } else { 116 if (selectedPage.getAnimationConfig) { 117 this.animationConfig.push({ 118 animatable: selectedPage, 119 type: 'entry' 120 }); 121 } 122 } 123 124 // configure oldPage animations iff exists. 125 if (oldPage) { 126 127 // cancel the currently running animation if one is ongoing. 128 if (oldPage.classList.contains('neon-animating')) { 129 this._squelchNextFinishEvent = true; 130 this.cancelAnimation(); 131 this._completeSelectedChanged(); 132 this._squelchNextFinishEvent = false; 133 } 134 135 // configure the animation. 136 if (this.exitAnimation) { 137 this.animationConfig.push({ 138 name: this.exitAnimation, 139 node: oldPage 140 }); 141 } else { 142 if (oldPage.getAnimationConfig) { 143 this.animationConfig.push({ 144 animatable: oldPage, 145 type: 'exit' 146 }); 147 } 148 } 149 150 // display the oldPage during the transition. 151 oldPage.classList.add('neon-animating'); 152 } 153 154 // display the selectedPage during the transition. 155 selectedPage.classList.add('neon-animating'); 156 157 // actually run the animations. 158 if (this.animationConfig.length >= 1) { 159 160 // on first load, ensure we run animations only after element is attached. 161 if (!this.isAttached) { 162 this.async(function () { 163 this.playAnimation(undefined, { 164 fromPage: null, 165 toPage: selectedPage 166 }); 167 }); 168 169 } else { 170 this.playAnimation(undefined, { 171 fromPage: oldPage, 172 toPage: selectedPage 173 }); 174 } 175 176 } else { 177 this._completeSelectedChanged(oldPage, selectedPage); 178 } 179 }, 180 181 /** 182 * @param {Object=} oldPage 183 * @param {Object=} selectedPage 184 */ 185 _completeSelectedChanged: function(oldPage, selectedPage) { 186 if (selectedPage) { 187 selectedPage.classList.remove('neon-animating'); 188 } 189 if (oldPage) { 190 oldPage.classList.remove('neon-animating'); 191 } 192 if (!selectedPage || !oldPage) { 193 var nodes = Polymer.dom(this.$.content).getDistributedNodes(); 194 for (var node, index = 0; node = nodes[index]; index++) { 195 node.classList && node.classList.remove('neon-animating'); 196 } 197 } 198 this.async(this._notifyPageResize); 199 }, 200 201 _onNeonAnimationFinish: function(event) { 202 if (this._squelchNextFinishEvent) { 203 this._squelchNextFinishEvent = false; 204 return; 205 } 206 this._completeSelectedChanged(event.detail.fromPage, event.detail.toPage); 207 }, 208 209 _notifyPageResize: function() { 210 var selectedPage = this.selectedItem || this._valueToItem(this.selected); 211 this.resizerShouldNotify = function(element) { 212 return element == selectedPage; 213 } 214 this.notifyResize(); 215 } 216 217 }) 218 219})(); 220</script> 221