Skip to content

Instantly share code, notes, and snippets.

@chukcha-wtf
Created September 18, 2015 20:24
Show Gist options
  • Save chukcha-wtf/3c042755cc000f9ce61d to your computer and use it in GitHub Desktop.
Save chukcha-wtf/3c042755cc000f9ce61d to your computer and use it in GitHub Desktop.
Example of using liquid-fire and ember-cli-f7 to animate page transitions
{!-- app/templates/-ios-navbar.hbs --}
{{#if f7.iosTheme}}
{{navbar-component currentPath=currentPath}}
{{/if}}
{!-- Materail navbar will have a similar structure --}
{!-- app/templates/about-navbar.hbs --}
<div class="left">
{{#link-to 'payment' class="back link icon-only external"}}
<i class="icon icon-back"></i>
{{/link-to}}
</div>
<div class="center">About</div>
{!-- app/templates/about.hbs --}
{{partial 'material-navbar'}}
<div class="page-content">
<div class="content-block-title">About Page</div>
{{#link-to 'index'}}Back{{/link-to}}
{!-- Some content here --}
</div>
// app/routes/about.js
// To be able to animate navbars on ios you'll need to include
// F7Route mixin to all your routes with navbars
import Ember from 'ember';
import F7Route from 'ember-cli-f7/mixins/f7-route';
export default Ember.Route.extend(F7Route, {
});
// config/environment.js
/* jshint node: true */
module.exports = function(environment) {
var ENV = {
// ... some config here
framework7: {
theme: 'ios', // supported themes 'ios' and 'material'
params: {
fastClicks: true,
cache: false, // turning off as we're not using F7 router
materialRipple: false, // turning off as it's slow on some Android devices
animatePages: false, // turning off as we're not using F7 router
preloadPreviousPage: false, // turning off as we're not using F7 router
}
}
}
return ENV;
};
{!-- app/templates/application.hbs --}
<div class="views">
<!-- Your main view, should have "view-main" class -->
<div class="view view-main">
{!--
We're using different navbars because depending on theme
they need to be placed either inside or outside .pages
Refer to documentation http://www.idangero.us/framework7/docs/app-layout.html
--}
{{partial 'ios-navbar'}}
<div class="pages {{if f7.iosTheme 'navbar-through' 'navbar-fixed'}}">
<!-- Pages container, because we use fixed-through navbar and toolbar, it has additional appropriate classes-->
{{liquid-outlet containerless="true" class="page"}}
</div>
</div>
</div>
{!-- app/templates/index-navbar.hbs --}
{!-- And provide specific templates which will be rendered in 'navbar' outlet --}
<div class="center">Index</div>
<div class="right">
{{f7-toggle-panel direction="left"}}
</div>
{!-- app/templates/index.hbs --}
{{partial 'material-navbar'}}
<div class="page-content">
<div class="content-block-title">Index Page</div>
{{#link-to 'about'}}About Page{{/link-to}}
{!-- Some content here --}
</div>
// app/routes/about.js
// To be able to animate navbars on ios you'll need to include
// F7Route mixin to all your routes with navbars
// 'sliding' animation from F7 isn't currently supported
import Ember from 'ember';
import F7Route from 'ember-cli-f7/mixins/f7-route';
export default Ember.Route.extend(F7Route, {
});
// app/components/liquid-versions.js
// Here I'm overriding one of liquid-fire components
// to be able to add Framework7 specific classes
// Hope some day I'll find time to refactor this code
import Ember from "ember";
import { containingElement } from "liquid-fire/ember-internals";
var get = Ember.get;
var set = Ember.set;
export default Ember.Component.extend({
tagName: "",
name: 'liquid-versions',
transitionMap: Ember.inject.service('liquid-fire-transitions'),
getTransitionDirection(versions, firstTime){
let transition = get(this, 'transitionMap').transitionFor({
versions: versions,
parentElement: Ember.$(containingElement(this)),
use: get(this, 'use'),
firstTime: firstTime ? 'yes' : 'no',
helperName: get(this, 'name'),
outletName: get(this, 'outletName')
});
return transition.animation && transition.animation.name || 'to-left';
},
didReceiveAttrs() {
this._super();
if (!this.versions || this._lastVersion !== this.getAttr('value')) {
this.appendVersion();
this._lastVersion = this.getAttr('value');
}
},
appendVersion() {
var versions = this.versions;
var firstTime = false;
var newValue = this.getAttr('value');
var oldValue;
if (!versions) {
firstTime = true;
versions = Ember.A();
} else {
oldValue = versions[0];
}
if (!firstTime && ((!oldValue && !newValue) ||
(oldValue === newValue))) {
return;
}
this.notifyContainer('willTransition', versions);
var newVersion = {
value: newValue,
shouldRender: newValue || get(this, 'renderWhenFalse')
};
versions.unshiftObject(newVersion);
this.direction = this.getTransitionDirection(versions, firstTime);
const direction = this.direction;
if (versions && versions.length > 1 && direction === 'to-left') {
versions.reverse();
}
this.firstTime = firstTime;
if (firstTime) {
set(this, 'versions', versions);
}
if (!newVersion.shouldRender && !firstTime) {
this._transition();
}
},
_transition: function() {
var versions = get(this, 'versions');
var firstTime = this.firstTime;
this.firstTime = false;
this.notifyContainer('afterChildInsertion', versions);
var inserted = Ember.$(containingElement(this)).find('.liquid-child');
var direction = this.direction;
inserted.css('visibility', 'visible');
if (!firstTime) {
var oldPageSelector = direction === 'to-left' ? 'first' : 'last';
var newPageSelector = direction === 'to-left' ? 'last' : 'first';
if (inserted.hasClass('navbar-inner')) {
this.animateNavbars(oldPageSelector, newPageSelector, versions, inserted, direction);
} else {
this.animatePages(oldPageSelector, newPageSelector, versions, inserted, direction);
}
}
},
animatePages(oldPageSelector, newPageSelector, versions, inserted, direction) {
var removeClasses = 'page-on-center page-on-right page-on-left';
var oldPage = Ember.$(containingElement(this)).find(`.page.liquid-child:${oldPageSelector}`);
var newPage = Ember.$(containingElement(this)).find(`.page.liquid-child:${newPageSelector}`);
var events = ['webkitAnimationEnd', 'OAnimationEnd', 'MSAnimationEnd', 'animationend'];
if (direction === 'to-left') {
oldPage.removeClass(removeClasses).addClass('page-from-center-to-left');
newPage.removeClass(removeClasses).addClass('page-from-right-to-center');
} else if (direction === 'to-right') {
newPage.removeClass(removeClasses).addClass('page-from-left-to-center');
oldPage.removeClass(removeClasses).addClass('page-from-center-to-right');
}
for (var i = events.length - 1; i >= 0; i--) {
var e = events[i];
newPage.on(e, () => {
this.finalizeVersions(versions, inserted);
this.notifyContainer("afterTransition", versions);
});
}
},
animateNavbars(oldPageSelector, newPageSelector, versions, inserted, direction) {
var removeClasses = 'navbar-on-right navbar-on-center navbar-on-left';
var oldNavbarInner = Ember.$(containingElement(this)).find(`.navbar-inner.liquid-child:${oldPageSelector}`);
var newNavbarInner = Ember.$(containingElement(this)).find(`.navbar-inner.liquid-child:${newPageSelector}`);
if (direction === 'to-left') {
newNavbarInner.removeClass(removeClasses).addClass('navbar-from-right-to-center');
oldNavbarInner.removeClass(removeClasses).addClass('navbar-from-center-to-left');
} else if (direction === 'to-right') {
newNavbarInner.removeClass(removeClasses).addClass('navbar-from-left-to-center');
oldNavbarInner.removeClass(removeClasses).addClass('navbar-from-center-to-right');
}
Ember.run.later(()=>{
this.finalizeVersions(versions, inserted);
}, 350);
},
finalizeVersions: function(versions, inserted) {
const toReplace = this.direction === 'to-left' ? 0 : 1;
versions.replace(toReplace, versions.length - 1);
if (inserted.hasClass('navbar-inner')) {
var newNavbarInner = Ember.$(containingElement(this)).find(`.navbar-inner.liquid-child`);
newNavbarInner.removeClass('navbar-from-right-to-center navbar-on-left navbar-on-right').addClass('navbar-on-center');
} else {
var newPage = Ember.$(containingElement(this)).find(`.page.liquid-child`);
var events = ['webkitAnimationEnd', 'OAnimationEnd', 'MSAnimationEnd', 'animationend'];
newPage.removeClass('page-from-right-to-center page-from-left-to-center page-on-right page-on-left').addClass('page-on-center');
for (var i = events.length - 1; i >= 0; i--) {
var e = events[i];
newPage.off(e);
}
}
},
notifyContainer: function(method, versions) {
var target = get(this, 'notify');
if (target) {
target.send(method, versions);
}
},
actions: {
childDidRender: function(child) {
var version = get(child, 'version');
set(version, 'view', child);
this._transition();
}
}
});
{!-- app/templates/components/navbar-component.hbs --}
{!-- app/components/navbar-component.js will be identical to f7-navbar component from the addon --}
{{#if f7.iosTheme}}
{{liquid-outlet 'navbar' containerless="true" class="navbar-inner"}}
{{else}}
<div class="navbar-inner">
{{outlet 'navbar'}}
</div>
{{/if}}
// app/transitions.js
// We're defining transitions direction just like it's done in Framework7
// direction 'to-left' for example means that we're sliding new page from the right
// (sliding current page to the left)
const pageTransitionDirection = 'to-left';
const pageTransitionBack = 'to-right';
const transitionWithReverse = function(route) {
this.transition(
this.toRoute(route),
this.use(pageTransitionDirection),
this.reverse(pageTransitionBack)
);
};
export default function(){
transitionWithReverse.call(this, 'about');
}
@chukcha-wtf
Copy link
Author

Here's that basic setup you'll need to successfully animate page transitions on iOS and Material themes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment