Ember iPhone style transitions
by amindunited
HTML
<script type="text/x-handlebars" data-template-name="application">
<div class="titleBar">
<img src="http://placekitten.com/50/50"/>
<h1>
Insert Title
</h1>
<img src="http://placekitten.com/50/50"/>
</div>
<nav class="mainNav">
<ul>
<li>{{#link-to 'index'}}Home{{/link-to}}</li>
<li>{{#link-to 'blog'}}Blog{{/link-to}}</li>
</ul>
</nav>
<div class="pageWrap">
{{outlet}}
</div>
</div>
</script>
<script type="text/x-handlebars" data-template-name="index">
<div {{bind-attr class=":page view.current view.previous view.next"}}>
<h2>Welcome page</h2>
</div>
</script>
<script type="text/x-handlebars" data-template-name="blog">
<div {{bind-attr class=":page view.current view.previous view.next"}}>
<h2>Blog</h2>
<nav>
{{#link-to 'post'}}First Article{{/link-to}}
</nav>
</div>
{{outlet}}
</script>
<!--
<script type="text/x-handlebars" data-template-name="blog/index">
<!--<div {{bind-attr class=":page view.current view.previous view.next"}}>-->
<!-- <h2>.... Index</h2> -->
<!-- </div>-->
<!-- {{outlet}}
</script>
-->
<script type="text/x-handlebars" data-template-name="post">
<div {{bind-attr class=":page view.current view.previous view.next"}}>
<h3>POST</h3>
{{#link-to 'details'}}Details{{/link-to}}
</div>
{{outlet}}
</script>
<script type="text/x-handlebars" data-template-name="details">
<div {{bind-attr class=":page view.current view.previous view.next"}}>
<h3>Details</h3>
</div>
{{outlet}}
</script>
<script src="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.3.0/handlebars.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/ember.js/1.5.1/ember.min.js"></script>
<script src="https://rawgithub.com/ebryn/ember-model/master/ember-model.js"></script>
<script...
CSS
body {
padding: 0px;
margin: 0px;
}
.titleBar {
background-color: #3d475f;
color: #FFFFFF;//#6D7A99;//#758099;
line-height: 50px;
height: 50px;
}
.titleBar img:first-of-type {
position: absolute;
top: 0px;
left: 0px;
}
.titleBar img:last-of-type {
position: absolute;
top: 0px;
right: 0px;
}
.titleBar > h1 {
margin:0px;
padding: 0px;
text-align: center;
}
.mainNav {
background-color: #14213E;//#758099;
}
.mainNav > ul { list-style-type:none; height: 38px; margin:0; padding:0;}
.mainNav > ul > li {
margin-top: 2px;
list-style-type:none;
height: 35px;
background-color: #758099;//#6D7A99;//#885258;//#B3B685;//#67694A;
padding-left: 5px;
padding-right: 5px;
float: left;
line-height: 35px;
min-width: 15%;
text-align:center;
border-left: solid 1px #14213E;
border-right: solid 1px #3d475f;
}
.mainNav > ul > li:first-of-type { border-top-left-radius: 10px; }
.mainNav > ul > li:last-of-type { border-top-right-radius: 10px; }
.mainNav > ul > li > a { color: #FFFFFF; font-weight: bold; }
.pageWrap {
position: absolute;
left: 0%;
top:90px;
width: 100%;
height: 100%;
border: solid 1px red;
}
/*
Page transitions
*/
.page {
/*margin-top: 90px;*/
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
position:absolute;
height: 100%;
width:100%;
top: 0px;
left: 0;
background-color: rgba(200, 200, 200, .5);
overflow: auto;
-webkit-overflow-scrolling: touch;
-webkit-transform: translate3d(0%, 0, 0);
transform: translate3d(0%, 0, 0);
}
.page.current {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
.page.previous {
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
}
.page.next {
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
}
.page.transition {
...
JavaScript
/* Request Animation Frame SHIM */
// shim layer with setTimeout fallback
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function( callback ){
window.setTimeout(callback, 1000 / 60);
};
})();
/* Deal with animation event prefixes */
var pfx = ["webkit", "moz", "MS", "o", ""];
function PrefixedEvent(element, type, callback) {
for (var p = 0; p < pfx.length; p++) {
if (!pfx[p]) type = type.toLowerCase();
element.addEventListener(pfx[p]+type, callback, false);
}
};
App = Em.Application.create();
App.ApplicationView = Em.View.extend({
current: true,
previous: false,
next: false,
});
App.Router.map(function () {
this.resource('blog', {path:'/blog'}, function(){
this.resource('post', {path:'/post'}, function(){
// this.resource('post', {path:':blog_id'}, function(){
this.resource('details', {path:'/details'}, function(){
});
});
});
});
App.ApplicationIndexView = Em.View.extend({
});
App.PageView = Em.View.extend({
current: false,
previous: false,
next: true,
animateIn: function(done){
console.log("Post view should animateIn");
this.get('parentView').set('previous', true);
this.get('parentView').set('next', false);
this.get('parentView').set('current', false);
var pp = this.get('parentView').$('.page').first();
pp.addClass('transition');
console.log("PP!", pp);
done.call(this);
PrefixedEvent(pp[0], 'transitionEnd', function(e){
$(e.target).removeClass('transition');
});
},
animateOut: function(done){
//
this.set('previous', false);
this.set('next', true);
this.set('current', false);
var pp = this.$('.page').first();
pp.addClass('transition');
console.log("PP!", pp);
PrefixedEvent(pp[0], 'transitionEnd',...