Home | History | Annotate | Download | only in neon-animation
      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