Make the most of Polymer's <neon-animated-pages>
effortlessly. NeonPageBehavior fires events allowing more control over a page's lifecycle, allows your page element to use a different animation-configuration when transitioning to each different page, and makes it possible to display a page as an overlay on top of another.
Please refer to the component page for more informations, or directly take a look at the demos.
- Why do you need NeonPageBehavior
- The page lifecycle
- Declaring different animation configurations
- Setup Overlay page
<neon-animated-pages>
is great for building
single-page application with engaging page transitions. However it currently has a few shortcomings:
-
Pages are not aware of their lifecycle, and a good amount of boilerplate is necessary to introduce this notion: A page including the
NeonPageBehavior
takes care of that for you, and can listen to events fired before and after both enter and exit transitions. -
You can only setup declaratively one transition, no matter how many pages your application is made up of: Using
NeonPageBehavior
, you can setup declaratively a different animation to transition to each and every page of you application. -
There is no built-in solution to make a page overlay another:
NeonPageBehavior
offers a solution to declaratively setup overlay pages, and thus plug them into you routing solution without any extra code.
An element being extended with the NeonPageBehavior
(and of course being a child of a
<neon-animated-pages>
element) can listen to 4 new events:
-
entry-animation-start
Called BEFORE the transition TO the page starts. Useful to handle initialization before your page gets visible (start loading data, animation optimisation,...). However it is not recommended to launch heavy processing at this time not to hinder the transition's animation.
-
entry-animation-finish
Called AFTER the transition TO the page finished. Useful to finish initializing your page (allow user focus,...).
-
exit-animation-start
Called BEFORE the transition FROM the page starts. Useful to deal with exit tasks (disallow user focus, animation optimisation,...). However it is not recommended to launch heavy processing at this time not to hinder the transition's animation.
-
exit-animation-finish
Called AFTER the transition FROM the page finished. Useful to handle exit tasks when your page isn't visible anymore (reset scroller position,...).
The detail
property of those dispatched events contains the following properties :
-
animationConfig
: TheanimationConfig
of the page (iethis
) for the transition. -
sharedElements
: ThesharedElements
of the page (iethis
) for the transition. -
from
: The identifier of the original page of the transition (ie the previously selected page), as inneon-animated-pages.selected
. -
fromPage
: The reference to the original page of the transition (ie the previously selected page). -
to
: The identifier of the destination page of the transition (ie the newly selected page), as inneon-animated-pages.selected
. -
toPage
: The reference to the destination page of the transition (ie the newly selected page),.
index.html
<neon-animated-pages class="flex" entry-animation="fade-in-animation" exit-animation="fade-out-animation">
<my-neon-page title="Page 1" style="background-color: red;"></my-neon-page>
<my-neon-page title="Page 2" style="background-color: blue;"></my-neon-page>
</neon-animated-pages>
my-neon-page.html
<dom-module id="my-neon-page">
<template>
<div>{{title}}</div>
</template>
<script>
Polymer({
is: 'my-neon-page',
behaviors: [
Polymer.NeonAnimatableBehavior,
Polymer.NeonPageBehavior
],
properties: {
title: {
type: String
}
},
listeners: {
'entry-animation-start': 'onEntryStart',
'entry-animation-finish': 'onEntryFinish',
'exit-animation-start': 'onExitStart',
'exit-animation-finish': 'onExitFinish'
},
onEntryStart: function(e) {
console.log(this.title + ' entry animation starts');
},
onEntryFinish: function(e) {
console.log(this.title + ' entry animation finished');
},
onExitStart: function(e) {
console.log(this.title + ' exit animation starts');
},
onExitFinish: function(e) {
console.log(this.title + ' exit animation finished');
}
...
Chances are your SPA's will contain more than a couple of pages.
Let's consider a simple SPA with 4 pages: page-list
displaying a list of items, page-detail
showing more information about a selected item, and finally page-settings-1
and page-settings-2
that are two general preference pages that can be access from all other pages.
It's very likely that you may want to implement a nice transition between page-list
and page-detail
, such as a hero animation, and something different for transitioning to the page-settings-1
and page-settings-2
.
Elements having the NeonPageBehavior
(and of course being child of a <neon-animated-pages>
) can let you do just that by supporting different animationConfig
properties for every single page transition, in addition to the global one.
index.html
<neon-animated-pages attr-for-selected="name">
<page-list name="list"></page-list>
<page-detail name="detail"></page-detail>
<page-settings-1 name="settings1"></page-settings-1>
<page-settings-2 name="settings2"></page-settings-2>
</neon-animated-pages>
page-list.html
// Specific animation config used when transitioning to/from `page-detail`
animationConfigDetail: {
type: Object,
value: function() {
return {
'entry': [{
name: 'hero-animation',
id: 'hero',
toPage: this
},
{
name: 'fade-in-animation',
node: this
}],
'exit': [{
name: 'hero-animation',
id: 'hero',
fromPage: this
},
{
name: 'fade-out-animation',
node: this
}]
};
}
},
// Shared elements when going to `page-detail`
sharedElementsDetail: {
type: Object,
value: function() {
return {
'hero': this.$.myHero
};
}
},
// Global animation config that will be used for any other page
// (`page-settings-1` and `page-settings-2` in our example)
animationConfig: {
type: Object,
value: function() {
return {
'entry': {
name: 'slide-from-bottom-animation',
node: this
},
'exit': {
name: 'slide-down-animation',
node: this
}
};
}
}
page-detail.html
// Specific animation config used when transitioning to/from `page-list`
animationConfigList: {
type: Object,
value: function() {
return {
'entry': [{
name: 'hero-animation',
id: 'hero',
toPage: this
},
{
name: 'fade-in-animation',
node: this
}],
'exit': [{
name: 'hero-animation',
id: 'hero',
fromPage: this
},
{
name: 'fade-out-animation',
node: this
}]
};
}
},
// Shared elements when going to `page-list`
sharedElementsList: {
type: Object,
value: function() {
return {
'hero': this.$.myHero
};
}
},
// Global animation config that will be used for any other page
// (`page-settings-1` and `page-settings-2` in our example)
animationConfig: {
type: Object,
value: function() {
return {
'entry': {
name: 'slide-from-bottom-animation',
node: this
},
'exit': {
name: 'slide-down-animation',
node: this
}
};
}
}
page-settings-1.html and page-settings-2.html
// Always the same transition animation for every page
animationConfig: {
type: Object,
value: function() {
return {
'entry': {
name: 'fade-in-animation',
node: this
},
'exit': {
name: 'fade-out-animation',
node: this
}
};
}
}
All animationConfig
properties must respect the following naming convention:
animationConfig
+ value representing the page to transition from/to for the parent <neon-animated-pages>
(see selected
and attrForSelected
properties in the
<neon-animated-pages>
documentation for more detail on this), all normalized to become a valid javascript variable name.
(ie if pageValue
='home-alone', the animationConfigHomeAlone
property will be used if it is defined, and animationConfig
if not).
If your element also have the NeonSharedElementAnimatableBehavior
, you can similarly
declare different sharedElements
properties for each different page to transition
from/to. The naming convention is the following:
sharedElements
+ value representing the page to transition from/to, all normalized to become a valid javascript variable name.
(ie if pageValue
='home-alone', the sharedElementsHomeAlone
property will be used if it is defined, and sharedElements
if not).
You can also differentiate the sharedElements
to use for the transition FROM a given page
and the sharedElements
to use for the transition TO a given page by following this naming convention:
sharedElements
+ value representing the page to transition from/to + Entry
or Exit
, all normalized to become a valid javascript variable name.
(ie if pageValue
='home-alone' and entering this page from the page 'home-alone', the sharedElementsHomeAloneEntry
property will be used if it is defined, sharedElementsHomeAlone
otherwise and sharedElements
if none of the 2 aforementioned properties are defined).
- Under the hood, this behavior swaps the
animationConfig
andsharedElements
properties of your page with the page-specific ones (for exampleanimationConfigMyPage
andsharedElementsMyPage
) for the duration of the transition. - A direct consequence of this implementation is that if you need to setup dynamic transitions, for example by using the clicked element of a list as the target node of an animation, you must modify the page-specific
animationConfig
andsharedElements
(for exampleanimationConfigMyPage
andsharedElementsMyPage
) and not the globalanimationConfig
andsharedElements
directly.
NeonPageBehavior
offers a solution to declaratively setup overlay pages, simply by adding the overlay-page
attribute to your page(s).
This will allow the page to be displayed over the previously selected one (which will be given the background-page
attribute).
No styling (other than setting the z-index
to appear on top) is done, so this remains entirely up to the developer.
index.html
<neon-animated-pages attr-for-selected="name">
<normal-page name="normalPage"></normal-page>
<overlay-page name="overlayPage" overlay-page></overlay-page>
</neon-animated-pages>
- It is not possible to display nested overlay pages (this is a pretty bad UX practice).
- For overlay pages to be displayed properly, all children (pages) of the
<neon-animated-pages>
must be including theNeonPageBehavior
. - An overlay page is styled to appear on top of the others by setting its CSS
z-index
property (to1001
). You must ensure no element in a background page has a higherz-index
. - If a page transitions to a
background-page
state (ie is displayed under an overlay page), the hypotheticalsharedElements
of this transition will be hidden automatically, until the page stops being abackground-page
. You may set thebackgroundPageShowsSharedElements
attribute if you want to prevent this (you probably do not, as thesharedElements
will then awkwardly "pop" back after the transition).