transition updates

This commit is contained in:
Adam Bradley
2015-05-05 08:53:59 -05:00
parent 478b7b83c6
commit 0e754ada16
12 changed files with 353 additions and 182 deletions

View File

@ -1,37 +1,38 @@
var _ = require('lodash')
var buildConfig = require('./scripts/build/config')
var SystemJsBuilder = require('systemjs-builder')
var exec = require('child_process').exec
var fs = require('fs')
var gulp = require('gulp')
var karma = require('karma').server
var path = require('path')
var VinylFile = require('vinyl')
var _ = require('lodash');
var buildConfig = require('./scripts/build/config');
var SystemJsBuilder = require('systemjs-builder');
var exec = require('child_process').exec;
var fs = require('fs');
var gulp = require('gulp');
var karma = require('karma').server;
var path = require('path');
var VinylFile = require('vinyl');
var argv = require('yargs').argv
var babel = require('gulp-babel')
var argv = require('yargs').argv;
var babel = require('gulp-babel');
var cached = require('gulp-cached');
var concat = require('gulp-concat')
var debug = require('gulp-debug')
var del = require('del')
var gulpif = require('gulp-if')
var karma = require('karma').server
var plumber = require('gulp-plumber')
var rename = require('gulp-rename')
var sass = require('gulp-sass')
var shell = require('gulp-shell')
var through2 = require('through2')
var traceur = require('gulp-traceur')
var concat = require('gulp-concat');
var debug = require('gulp-debug');
var del = require('del');
var gulpif = require('gulp-if');
var karma = require('karma').server;
var plumber = require('gulp-plumber');
var rename = require('gulp-rename');
var sass = require('gulp-sass');
var shell = require('gulp-shell');
var through2 = require('through2');
var traceur = require('gulp-traceur');
require('./scripts/snapshot/snapshot.task')(gulp, argv, buildConfig)
require('./scripts/snapshot/snapshot.task')(gulp, argv, buildConfig);
gulp.task('default', ['clean'], function() {
gulp.run('build')
})
gulp.run('build');
});
gulp.task('build', ['e2e', 'ionic-js', 'ng2', 'sass'])
gulp.task('build', ['e2e', 'ionic-js', 'ng2', 'sass']);
gulp.task('lib', ['fonts', 'dependencies'])
gulp.task('lib', ['fonts', 'dependencies']);
gulp.task('watch', ['default'], function() {
gulp.watch(buildConfig.src.scss, ['sass'])
@ -43,14 +44,15 @@ gulp.task('watch', ['default'], function() {
buildConfig.src.e2e, buildConfig.src.html,
'scripts/e2e/index.template.html'
), ['ionic-js'])
})
});
gulp.task('karma', function() {
return karma.start({ configFile: __dirname + '/scripts/test/karma.conf.js' })
})
});
gulp.task('karma-watch', function() {
return karma.start({ configFile: __dirname + '/scripts/test/karma-watch.conf.js' })
})
});
gulp.task('dependencies', function() {
var copyFrom = buildConfig.scripts
@ -58,7 +60,7 @@ gulp.task('dependencies', function() {
.filter(function(item) { return !!item; });
return gulp.src(copyFrom)
.pipe(gulp.dest(buildConfig.distLib))
})
});
gulp.task('sass', function() {
return gulp.src('ionic/ionic.scss')
@ -68,16 +70,16 @@ gulp.task('sass', function() {
}
}))
.pipe(gulp.dest('dist/css'));
})
});
gulp.task('fonts', function() {
return gulp.src('ionic/components/icon/fonts/**/*')
.pipe(gulp.dest('dist/fonts'));
})
});
gulp.task('clean', function(done) {
del([buildConfig.dist], done)
})
del([buildConfig.dist], done);
});
gulp.task('e2e', ['ionic-js', 'sass'], function() {
var indexContents = _.template( fs.readFileSync('scripts/e2e/index.template.html') )({
@ -89,7 +91,7 @@ gulp.task('e2e', ['ionic-js', 'sass'], function() {
'android',
'core',
'ios',
]
];
// Get each test folder with gulp.src
return gulp.src(buildConfig.src.e2e)
@ -163,13 +165,14 @@ gulp.task('e2e', ['ionic-js', 'sass'], function() {
})
}
})
});
gulp.task('ng2-copy', function() {
return gulp.src('node_modules/angular2/es6/prod/**/*.es6')
.pipe(rename({ extname: '.js' }))
.pipe(gulp.dest(path.join(buildConfig.distLib, 'angular2')));
});
gulp.task('ng2', ['lib', 'ng2-copy'], function() {
var builder = new SystemJsBuilder({
paths: {
@ -181,6 +184,7 @@ gulp.task('ng2', ['lib', 'ng2-copy'], function() {
return builder.build('angular2/di', path.join(buildConfig.distLib, 'angular2-di.js'));
});
});
gulp.task('ng2-di', ['ng2'], function() {
});
@ -201,36 +205,4 @@ gulp.task('ionic-js', function() {
}
});
return builder.build('ionic/ionic', path.join(buildConfig.distLib, 'ionic2.js'));
})
// Take es6 files from angular2's output, rename to js, and move to dist/lib/
// gulp.task('ng2-rename', function(done) {
// exec('test -e node_modules/angular-master', function(err) {
// if (err) {
// console.log('You have not installed angular master.\n' +
// 'Please run ./scripts/build/update-angular.sh.\n' +
// 'Aborting.')
// return process.exit(1)
// }
// gulp.src([
// 'node_modules/angular-master/dist/js/dev/es6/{angular2,rtts_assert}/**/*.es6'
// ])
// .pipe(rename({ extname: '.js' }))
// .pipe(gulp.dest(buildConfig.distLib))
// .on('end', done)
// })
// })
// // We use SystemJsBuilder to build ng2 because it will properly
// gulp.task('ng2', ['ng2-rename'], function() {
// var builder = new SystemJsBuilder()
// builder.config({
// baseURL: buildConfig.distLib,
// traceurOptions: buildConfig.traceurOptions,
// map: {
// rx: __dirname + '/node_modules/rx'
// }
// })
// return builder.build('angular2/angular2', buildConfig.distLib + '/angular2.js')
// })
});

View File

@ -1,31 +0,0 @@
import {Animation} from '../collide/animation';
import {addEasing} from '../collide/easing';
const easing = [.36, .66, .04, 1];
const duration = 500;
class IOSTransition extends Animation {
constructor(navCtrl) {
this.duration(duration);
addEasing('ios', easing);
this.easing('ios');
var enteringViewEle = navCtrl.enteringEle();
var viewA = new Animation();
viewA.elements( document.querySelectorAll('.square') )
.to('translateX', translateX)
this.addChild(row1);
}
}

View File

@ -1,8 +1,19 @@
import {NgElement} from 'angular2/angular2';
import * as util from 'ionic/util';
import {Transition} from 'ionic/ionic';
const STAGED_STATE = 'staged';
const STAGED_ENTERING_STATE = 'staged-enter';
const STAGED_LEAVING_STATE = 'staged-leave';
const ACTIVELY_ENTERING_STATE = 'entering';
const ACTIVELY_LEAVING_STATE = 'leaving';
const ACTIVE_STATE = 'active';
const CACHED_STATE = 'cached';
/*
* Used be tabs and nav
* Used by tabs and nav
*/
export class NavBase {
constructor(
@ -23,47 +34,174 @@ export class NavBase {
containsClass(Class) {
for (let i = 0; i < this._stack.length; i++) {
if (this._stack[i].Class === Class) {
return true
return true;
}
}
return false
return false;
}
set initial(Class) {
if (!this.initialized) {
this.initialized = true
this.push(Class, {}, {
animation: 'none'
});
this.push(Class);
}
}
/**
* Push a new view into the history stack.
*/
getActiveItem() {
for (let i = 0, ii = this.navItems.length; i < ii; i++) {
if (this.navItems[i].state === ACTIVE_STATE) {
return this.navItems[i];
}
}
return null;
}
getStagedEnteringItem() {
for (let i = 0, ii = this.navItems.length; i < ii; i++) {
if (this.navItems[i].state === STAGED_ENTERING_STATE) {
return this.navItems[i];
}
}
return null;
}
getStagedLeavingItem() {
for (let i = 0, ii = this.navItems.length; i < ii; i++) {
if (this.navItems[i].state === STAGED_LEAVING_STATE) {
return this.navItems[i];
}
}
return null;
}
getLeavingItems() {
let items = [];
for (let i = 0, ii = this.navItems.length; i < ii; i++) {
if (this.navItems[i].state === ACTIVELY_LEAVING_STATE || this.navItems[i].state === STAGED_LEAVING_STATE) {
items.push(this.navItems[i]);
}
}
return items;
}
push(Class: Function, params = {}, opts = {}) {
let pushedItem = new NavStackData(Class, params);
let resolve;
let promise = new Promise(res => { resolve = res; });
this._stack.push(pushedItem);
this.navItems.push(pushedItem);
// default the direction to "forward"
opts.direction = opts.direction || 'forward';
return pushedItem.setup().then(() => {
let current = this._getPrevious(pushedItem);
current && current.leaveReverse(opts);
return pushedItem.enter(opts);
});
// do not animate if this is the first in the stack
if (!this._stack.length) {
opts.animation = 'none';
}
/**
* Pop a view off the history
*/
pop(opts = {}) {
let current = this._stack.pop()
let dest = this.last()
// the active item is going to be the leaving one (if one exists)
let leavingItem = this.getActiveItem() || {};
dest && dest.enterReverse(opts)
return current && current.leave(opts)
.then(() => this._destroy(current))
// create a new NavStackItem
let enteringItem = new NavStackItem(Class, params);
// set that this item is staged (it's not ready to be animated in yet)
enteringItem.state = STAGED_STATE;
// add the item to the stack (just renders in the DOM, doesn't animate yet)
this._stack.push(enteringItem);
this.navItems.push(enteringItem);
// start the transition
this.transition(enteringItem, leavingItem, opts).then(() => {
console.log('push completed');
resolve();
});
return promise;
}
pop(opts = {}) {
let resolve;
let promise = new Promise(res => { resolve = res; });
// default the direction to "back"
opts.direction = opts.direction || 'back';
// remove the last item
this._stack.pop();
// the entering item is now the new last item
let enteringItem = this.last()
// get the active item and set that it is staged to be leaving
// was probably the one popped from the stack
let leavingItem = this.getActiveItem() || {};
// start the transition
this.transition(enteringItem, leavingItem, opts).then(() => {
// transition completed, destroy the leaving item
console.log('pop completed');
this._destroy(leavingItem);
resolve();
});
return promise;
}
transition(enteringItem, leavingItem, opts) {
let resolve;
let promise = new Promise(res => { resolve = res; });
// wait for the new item to complete setup
enteringItem.setup().then(() => {
// get any items that are already staged to leave, or are actively leaving
// since a different item will be leaving, reset any actively leaving items to cached
let leavingItems = this.getLeavingItems();
for (let i = 0; i < leavingItems.length; i++) {
leavingItems[i].state = CACHED_STATE;
}
// set that the leaving item is stage to be leaving
leavingItem.state = STAGED_LEAVING_STATE;
// set that the new item pushed on the stack is staged to be entering
// setting staged state is important for the transition logic to find the correct item
enteringItem.state = STAGED_ENTERING_STATE;
// init the transition animation
let transAnimation = Transition.create(this, opts);
// wait for the items to be fully staged
transAnimation.stage().then(() => {
// update the state that the items are actively entering/leaving
enteringItem.state = ACTIVELY_ENTERING_STATE;
leavingItem.state = ACTIVELY_LEAVING_STATE;
// start the transition
transAnimation.start().then(() => {
// transition has completed, update each item's state
enteringItem.state = ACTIVE_STATE;
leavingItem.state = CACHED_STATE;
// resolve that this push has completed
resolve();
// on the next frame, hide the item that's cached
util.dom.raf(() => {
// ensure it's state is still cached
if (leavingItem.state === CACHED_STATE && leavingItem.navItem) {
// CSS default is display:none, so setting to '' does the trick
leavingItem.navItem.domElement.style.display = '';
}
});
});
});
});
return promise;
}
last() {
@ -103,23 +241,18 @@ export class NavBase {
return this.pop(opts)
}
setStack(stack) {
this._stack = stack.slice()
this.navItems = stack.slice()
}
remove(index) {
const item = this._stack[index]
this._stack.splice(index, 1)
this._destroy(item)
const item = this._stack[index];
this._stack.splice(index, 1);
this._destroy(item);
}
_destroy(navItem) {
util.array.remove(this.navItems, navItem)
util.array.remove(this.navItems, navItem);
}
_getPrevious(item) {
return this._stack[ this._stack.indexOf(item) - 1 ]
getPrevious(item) {
return this._stack[ this._stack.indexOf(item) - 1 ];
}
getToolbars(pos: String) {
@ -128,7 +261,8 @@ export class NavBase {
}
}
class NavStackData {
class NavStackItem {
constructor(ComponentClass, params = {}) {
this.Class = ComponentClass;
this.params = params;

View File

@ -1,5 +1,5 @@
<h1>First Page</h1>
<h1 style="background:blue">First Page</h1>
<p>
<button (click)="push()">Push (Go to 2nd)</button>

View File

@ -1,5 +1,5 @@
<h1>Second Page</h1>
<h1 style="background:green">Second Page</h1>
<p>
<button (click)="pop()">Pop (Go back to 1st)</button>

View File

@ -1,5 +1,5 @@
<h1>Third Page</h1>
<h1 style="background:yellow">Third Page</h1>
<p>
<button (click)="pop()">Pop (Go back to 2nd)</button>

View File

@ -33,8 +33,12 @@ export * from 'ionic/components'
export * from 'ionic/platform/platform'
export * from 'ionic/routing/router'
export * from 'ionic/util/focus'
export * from 'ionic/collide/animation'
export * from 'ionic/engine/engine'
export * from 'ionic/engine/cordova/cordova'
export * from 'ionic/engine/electron/electron'
export * from 'ionic/collide/animation'
export * from 'ionic/transitions/transition'
export * from 'ionic/transitions/none-transition'
export * from 'ionic/transitions/ios-transition'

View File

@ -0,0 +1,57 @@
import {Animation} from '../collide/animation';
import {addEasing} from '../collide/easing';
import {rafPromise} from '../util/dom'
import {Transition} from './transition'
const EASING_FN = [.36, .66, .04, 1];
const DURATION = 500;
class IOSTransition extends Animation {
constructor(navCtrl, opts) {
super();
this.duration(DURATION);
this.easing('ios');
// get the entering and leaving items
this.enteringItem = navCtrl.getStagedEnteringItem();
this.leavingItem = navCtrl.getStagedLeavingItem();
// create animation for entering item
let enteringItemAnimation = new Animation();
enteringItemAnimation.elements(this.enteringItem.navItem.domElement);
// show the item
this.enteringItem.navItem.domElement.style.display = 'block';
if (opts.direction === 'back') {
// back direction
this.enteringItem.navItem.domElement.style.transform = 'translateX(-33%)';
if (this.leavingItem) {
this.leavingItem.navItem.domElement.style.display = '';
}
} else {
// forward direction
this.enteringItem.navItem.domElement.style.transform = 'translateX(100%)';
}
// entering item moves to dead center
enteringItemAnimation.to('translateX', ['0%', '100%']);
this.addChild(enteringItemAnimation);
}
stage() {
return rafPromise();
}
}
addEasing('ios', EASING_FN);
Transition.register('ios', IOSTransition);

View File

@ -0,0 +1,34 @@
import {Transition} from './transition'
class NoneTransition {
constructor(navCtrl, opts) {
// get the entering and leaving items
let enteringItem = navCtrl.getStagedEnteringItem();
let leavingItem = navCtrl.getStagedLeavingItem();
// show the entering item
enteringItem.navItem.domElement.style.display = 'block';
enteringItem.navItem.domElement.style.transform = 'translateX(0%)';
// hide the leaving item
if (leavingItem && leavingItem.navItem) {
leavingItem.navItem.domElement.style.display = '';
}
}
stage() {
// immediately resolve
return Promise.resolve();
}
start() {
// immediately resolve
return Promise.resolve();
}
}
Transition.register('none', NoneTransition);

View File

@ -0,0 +1,23 @@
let registry = {};
class TransitionController {
create(navCtrl, opts = {}) {
let name = opts.animation || 'ios';
let TransitionClass = registry[name];
if (!TransitionClass) {
TransitionClass = registry['none'];
}
return new TransitionClass(navCtrl, opts);
}
register(name, transitionClass) {
registry[name] = transitionClass;
}
}
export let Transition = new TransitionController();

View File

@ -1,4 +1,4 @@
export function noop() {}
export function noop() {};
export function clamp(min, n, max) {
return Math.max(min, Math.min(n, max));
@ -54,27 +54,27 @@ export const isArray = Array.isArray
export function pascalCaseToDashCase(str = '') {
return str.charAt(0).toLowerCase() + str.substring(1).replace(/[A-Z]/g, match => {
return '-' + match.toLowerCase()
})
return '-' + match.toLowerCase();
});
}
let uid = 0
let uid = 0;
export function nextUid() {
return ++uid;
}
export class Log {
static log(...args) {
console.log.apply(console, args)
console.log.apply(console, args);
}
static info(...args) {
console.info.apply(console, args)
console.info.apply(console, args);
}
static warn(...args) {
console.warn.apply(console, args)
console.warn.apply(console, args);
}
static error(...args) {
console.error.apply(console, args)
console.error.apply(console, args);
}
}
@ -85,12 +85,12 @@ export let array = {
}
},
remove(arr, item) {
const index = arr.indexOf(item)
const index = arr.indexOf(item);
if (index === -1) {
return false
return false;
}
arr.splice(index, 1)
return true
arr.splice(index, 1);
return true;
}
}
@ -101,8 +101,8 @@ export function getQuerystring(key) {
const queries = window.location.href.slice(startIndex + 1).split('&');
if (queries.length) {
queries.forEach((param) => {
var split = param.split('=')
queryParams[split[0]] = split[1]
var split = param.split('=');
queryParams[split[0]] = split[1];
});
}
}

View File

@ -1,22 +0,0 @@
#!/bin/sh
NG_FOLDER=node_modules/angular-master
cd $(dirname $0)/../../
$(test -e $NG_FOLDER) || (git clone git@github.com:angular/angular $NG_FOLDER)
cd $NG_FOLDER
echo "Pulling from master.."
git pull origin master
echo "Running npm install in angular2..."
npm install
echo "Running gulp to build source..."
gulp clean
gulp build.js
echo "--"
echo "-- DONE, gulp will work now --"
echo "--"