• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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<link rel="import" href="animations/opaque-animation.html">
15
16<!--
17Material 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
20children pages should implement `Polymer.NeonAnimatableBehavior` and define `entry` and `exit`
21animations 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