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