mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-20 12:29:55 +08:00
@ -243,7 +243,7 @@ export class Animation {
|
||||
|
||||
// set the async TRANSITION END event
|
||||
// and run onFinishes when the transition ends
|
||||
self._asyncEnd(duration);
|
||||
self._asyncEnd(duration, true);
|
||||
|
||||
// begin each animation when everything is rendered in their place
|
||||
// and the transition duration/easing is ready to go
|
||||
@ -275,7 +275,7 @@ export class Animation {
|
||||
|
||||
// since there was no animation, it's done
|
||||
// fire off all the onFinishes
|
||||
self._onFinish();
|
||||
self._onFinish(true);
|
||||
}
|
||||
}
|
||||
|
||||
@ -299,7 +299,7 @@ export class Animation {
|
||||
|
||||
// set the async TRANSITION END event
|
||||
// and run onFinishes when the transition ends
|
||||
self._asyncEnd(duration);
|
||||
self._asyncEnd(duration, false);
|
||||
|
||||
} else {
|
||||
// this animation does not have a duration, so it should not animate
|
||||
@ -308,15 +308,15 @@ export class Animation {
|
||||
|
||||
// since there was no animation, it's done
|
||||
// fire off all the onFinishes
|
||||
self._onFinish();
|
||||
self._onFinish(false);
|
||||
}
|
||||
}
|
||||
|
||||
_asyncEnd(duration: number) {
|
||||
_asyncEnd(duration: number, shouldComplete: boolean) {
|
||||
var self = this;
|
||||
|
||||
function onTransitionEnd(ev) {
|
||||
console.debug('Animation async end,', (ev ? 'transitionEnd event' : 'fallback timeout'));
|
||||
console.debug('Animation async end,', (ev ? 'transitionEnd, ' + ev.target.nodeName + ', property: ' + ev.propertyName : 'fallback timeout'));
|
||||
|
||||
// ensure transition end events and timeouts have been cleared
|
||||
self._clearAsync();
|
||||
@ -324,7 +324,7 @@ export class Animation {
|
||||
// set the after styles
|
||||
self._after();
|
||||
self._willChange(false);
|
||||
self._onFinish();
|
||||
self._onFinish(shouldComplete);
|
||||
}
|
||||
|
||||
// set the TRANSITION END event on one of the transition elements
|
||||
@ -580,12 +580,12 @@ export class Animation {
|
||||
// for example, the left menu was dragged all the way open already
|
||||
this._after();
|
||||
this._willChange(false);
|
||||
this._onFinish();
|
||||
this._onFinish(shouldComplete);
|
||||
|
||||
} else {
|
||||
// the stepValue was left off at a point when it needs to finish transition still
|
||||
// for example, the left menu was opened 75% and needs to finish opening
|
||||
this._asyncEnd(64);
|
||||
this._asyncEnd(64, shouldComplete);
|
||||
|
||||
// force quick duration, linear easing
|
||||
this._setTrans(64, true);
|
||||
@ -611,15 +611,15 @@ export class Animation {
|
||||
return this;
|
||||
}
|
||||
|
||||
_onFinish() {
|
||||
_onFinish(hasCompleted: boolean) {
|
||||
this.isPlaying = false;
|
||||
var i;
|
||||
|
||||
for (i = 0; i < this._fFns.length; i++) {
|
||||
this._fFns[i]();
|
||||
this._fFns[i](hasCompleted);
|
||||
}
|
||||
for (i = 0; i < this._fOnceFns.length; i++) {
|
||||
this._fOnceFns[i]();
|
||||
this._fOnceFns[i](hasCompleted);
|
||||
}
|
||||
this._fOnceFns = [];
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Compiler, ElementRef, Injector, provide, NgZone, AppViewManager, Renderer, ResolvedProvider, Type} from 'angular2/core';
|
||||
import {Compiler, ElementRef, Injector, provide, NgZone, AppViewManager, Renderer, ResolvedProvider, Type, Input} from 'angular2/core';
|
||||
import {wtfLeave, wtfCreateScope, WtfScopeFn, wtfStartTimeRange, wtfEndTimeRange} from 'angular2/instrumentation';
|
||||
|
||||
import {Config} from '../../config/config';
|
||||
@ -7,7 +7,7 @@ import {IonicApp} from '../app/app';
|
||||
import {Keyboard} from '../../util/keyboard';
|
||||
import {NavParams} from './nav-params';
|
||||
import {NavRouter} from './nav-router';
|
||||
import {pascalCaseToDashCase} from '../../util/util';
|
||||
import {pascalCaseToDashCase, isTrueProperty} from '../../util/util';
|
||||
import {raf} from '../../util/dom';
|
||||
import {SwipeBackGesture} from './swipe-back';
|
||||
import {Transition} from '../../transitions/transition';
|
||||
@ -106,11 +106,12 @@ import {ViewController} from './view-controller';
|
||||
export class NavController extends Ion {
|
||||
private _transIds = 0;
|
||||
private _init = false;
|
||||
private _lastTrans: Transition;
|
||||
private _trans: Transition;
|
||||
private _sbGesture: SwipeBackGesture;
|
||||
private _sbEnabled: boolean;
|
||||
private _sbThreshold: number;
|
||||
|
||||
protected _ids: number = -1;
|
||||
protected _sbEnabled: any;
|
||||
protected _sbThreshold: any;
|
||||
protected _sbTrans: Transition = null;
|
||||
protected _trnsDelay: any;
|
||||
protected _trnsTime: number = 0;
|
||||
protected _views: Array<ViewController> = [];
|
||||
@ -133,12 +134,7 @@ export class NavController extends Ion {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
sbGesture: any;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
parent;
|
||||
parent: any;
|
||||
|
||||
/**
|
||||
* @private
|
||||
@ -164,7 +160,7 @@ export class NavController extends Ion {
|
||||
|
||||
this._trnsDelay = config.get('pageTransitionDelay');
|
||||
|
||||
this._sbEnabled = config.get('swipeBackEnabled') || false;
|
||||
this._sbEnabled = config.getBoolean('swipeBackEnabled') || false;
|
||||
this._sbThreshold = config.get('swipeBackThreshold') || 40;
|
||||
|
||||
this.id = ++ctrlIds;
|
||||
@ -261,7 +257,7 @@ export class NavController extends Ion {
|
||||
*/
|
||||
setPages(pages: Array<{page: Type, params?: any}>, opts: NavOptions = {}): Promise<any> {
|
||||
if (!pages || !pages.length) {
|
||||
return Promise.resolve();
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
// deprecated warning
|
||||
@ -296,9 +292,9 @@ export class NavController extends Ion {
|
||||
let promise = new Promise(res => { resolve = res; });
|
||||
|
||||
// start the transition, fire resolve when done...
|
||||
this._transition(enteringView, leavingView, opts, () => {
|
||||
this._transition(enteringView, leavingView, opts, (hasCompleted: boolean) => {
|
||||
// transition has completed!!
|
||||
resolve(enteringView);
|
||||
resolve(hasCompleted);
|
||||
});
|
||||
|
||||
return promise;
|
||||
@ -533,9 +529,9 @@ export class NavController extends Ion {
|
||||
let leavingView = this.getByState(STATE_INIT_LEAVE);
|
||||
|
||||
// start the transition, fire resolve when done...
|
||||
this._transition(enteringView, leavingView, opts, () => {
|
||||
this._transition(enteringView, leavingView, opts, (hasCompleted: boolean) => {
|
||||
// transition has completed!!
|
||||
resolve(enteringView);
|
||||
resolve(hasCompleted);
|
||||
});
|
||||
|
||||
return promise;
|
||||
@ -697,21 +693,21 @@ export class NavController extends Ion {
|
||||
opts.animation = forcedActive.getTransitionName(opts.direction);
|
||||
}
|
||||
|
||||
if (this._lastTrans) {
|
||||
this._lastTrans
|
||||
if (this._trans) {
|
||||
this._trans
|
||||
.onFinish(() => {
|
||||
opts.animate = false;
|
||||
this._transition(forcedActive, null, opts, () => {
|
||||
this._transition(forcedActive, null, opts, (hasCompleted: boolean) => {
|
||||
// transition has completed!!
|
||||
resolve();
|
||||
resolve(hasCompleted);
|
||||
});
|
||||
}, false, true)
|
||||
.stop();
|
||||
this._lastTrans.destroy();
|
||||
this._lastTrans = null;
|
||||
this._trans.destroy();
|
||||
this._trans = null;
|
||||
|
||||
} else {
|
||||
resolve();
|
||||
resolve(false);
|
||||
}
|
||||
|
||||
return promise;
|
||||
@ -732,9 +728,9 @@ export class NavController extends Ion {
|
||||
let enteringView = this.getByState(STATE_INIT_ENTER);
|
||||
|
||||
// start the transition, fire resolve when done...
|
||||
this._transition(enteringView, leavingView, opts, () => {
|
||||
this._transition(enteringView, leavingView, opts, (hasCompleted: boolean) => {
|
||||
// transition has completed!!
|
||||
resolve();
|
||||
resolve(hasCompleted);
|
||||
});
|
||||
|
||||
return promise;
|
||||
@ -744,7 +740,7 @@ export class NavController extends Ion {
|
||||
// there's still an active view after _remove() figured out states
|
||||
// so this means views that were only removed before the active
|
||||
// view, so auto-resolve since no transition needs to happen
|
||||
return Promise.resolve();
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -856,8 +852,8 @@ export class NavController extends Ion {
|
||||
|
||||
if (enteringView === leavingView) {
|
||||
// if the entering view and leaving view are the same thing don't continue
|
||||
this._transComplete(transId, enteringView, leavingView, null);
|
||||
return done(enteringView);
|
||||
this._transFinish(transId, enteringView, leavingView, null, false);
|
||||
return done(false);
|
||||
}
|
||||
|
||||
// lets time this sucker, ready go
|
||||
@ -888,10 +884,10 @@ export class NavController extends Ion {
|
||||
*/
|
||||
|
||||
// begin the multiple async process of transitioning to the entering view
|
||||
this._render(transId, enteringView, leavingView, opts, () => {
|
||||
this._transComplete(transId, enteringView, leavingView, opts.direction);
|
||||
this._render(transId, enteringView, leavingView, opts, (hasCompleted: boolean) => {
|
||||
this._transFinish(transId, enteringView, leavingView, opts.direction, hasCompleted);
|
||||
wtfEndTimeRange(wtfScope);
|
||||
done(enteringView);
|
||||
done(hasCompleted);
|
||||
});
|
||||
}
|
||||
|
||||
@ -985,19 +981,13 @@ export class NavController extends Ion {
|
||||
enteringView.willEnter();
|
||||
leavingView.willLeave();
|
||||
|
||||
// lifecycle events may have updated some data
|
||||
// wait one frame and allow the raf to do a change detection
|
||||
// before kicking off the transition and showing the new view
|
||||
raf(() => {
|
||||
this._beforeTrans(enteringView, leavingView, opts, done);
|
||||
});
|
||||
|
||||
} else {
|
||||
// this view is being preloaded, don't call lifecycle events
|
||||
// transition does not need to animate
|
||||
opts.animate = false;
|
||||
this._beforeTrans(enteringView, leavingView, opts, done);
|
||||
}
|
||||
|
||||
this._beforeTrans(enteringView, leavingView, opts, done);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1034,8 +1024,8 @@ export class NavController extends Ion {
|
||||
leavingView,
|
||||
transitionOpts);
|
||||
|
||||
this._lastTrans && this._lastTrans.destroy();
|
||||
this._lastTrans = transAnimation;
|
||||
this._trans && this._trans.destroy();
|
||||
this._trans = transAnimation;
|
||||
|
||||
if (opts.animate === false) {
|
||||
// force it to not animate the elements, just apply the "to" styles
|
||||
@ -1054,24 +1044,34 @@ export class NavController extends Ion {
|
||||
}
|
||||
|
||||
// create a callback for when the animation is done
|
||||
transAnimation.onFinish(() => {
|
||||
transAnimation.onFinish((hasCompleted: boolean) => {
|
||||
// transition animation has ended
|
||||
|
||||
// dispose the animation and it's element references
|
||||
// destroy the animation and it's element references
|
||||
transAnimation.destroy();
|
||||
|
||||
this._afterTrans(enteringView, leavingView, opts, done);
|
||||
this._afterTrans(enteringView, leavingView, opts, hasCompleted, done);
|
||||
});
|
||||
|
||||
// cool, let's do this, start the transition
|
||||
transAnimation.play();
|
||||
if (opts.progressAnimation) {
|
||||
// this is a swipe to go back, just get the transition progress ready
|
||||
// kick off the swipe animation start
|
||||
transAnimation.progressStart();
|
||||
|
||||
} else {
|
||||
|
||||
// this is a normal animation
|
||||
// kick it off and let it play through
|
||||
transAnimation.play();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private _afterTrans(enteringView: ViewController, leavingView: ViewController, opts: NavOptions, done: Function) {
|
||||
private _afterTrans(enteringView: ViewController, leavingView: ViewController, opts: NavOptions, hasCompleted: boolean, done: Function) {
|
||||
// transition has completed, update each view's state
|
||||
// place back into the zone, run didEnter/didLeave
|
||||
// call the final callback when done
|
||||
@ -1079,7 +1079,7 @@ export class NavController extends Ion {
|
||||
// run inside of the zone again
|
||||
this._zone.run(() => {
|
||||
|
||||
if (!opts.preload) {
|
||||
if (!opts.preload && hasCompleted) {
|
||||
enteringView.didEnter();
|
||||
leavingView.didLeave();
|
||||
}
|
||||
@ -1087,7 +1087,7 @@ export class NavController extends Ion {
|
||||
if (enteringView.state === STATE_INACTIVE) {
|
||||
// this entering view is already set to inactive, so this
|
||||
// transition must be canceled, so don't continue
|
||||
return done();
|
||||
return done(hasCompleted);
|
||||
}
|
||||
|
||||
if (opts.keyboardClose !== false && this._keyboard.isOpen()) {
|
||||
@ -1097,12 +1097,12 @@ export class NavController extends Ion {
|
||||
this._keyboard.onClose(() => {
|
||||
|
||||
// keyboard has finished closing, transition complete
|
||||
done();
|
||||
done(hasCompleted);
|
||||
}, 32);
|
||||
|
||||
} else {
|
||||
// all good, transition complete
|
||||
done();
|
||||
done(hasCompleted);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1110,51 +1110,67 @@ export class NavController extends Ion {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private _transComplete(transId: number, enteringView: ViewController, leavingView: ViewController, direction: string) {
|
||||
private _transFinish(transId: number, enteringView: ViewController, leavingView: ViewController, direction: string, hasCompleted: boolean) {
|
||||
// a transition has completed, but not sure if it's the last one or not
|
||||
// check if this transition is the most recent one or not
|
||||
|
||||
if (transId === this._transIds) {
|
||||
// ok, good news, there were no other transitions that kicked
|
||||
// off during the time this transition started and ended
|
||||
// so the entering one is now officially the active transition
|
||||
// and the leaving transition is now just inactive
|
||||
|
||||
if (enteringView.state !== STATE_REMOVE_AFTER_TRANS) {
|
||||
enteringView.state = STATE_ACTIVE;
|
||||
}
|
||||
if (hasCompleted) {
|
||||
// this transition has completed as normal
|
||||
// so the entering one is now the active view
|
||||
// and the leaving view is now just inactive
|
||||
if (enteringView.state !== STATE_REMOVE_AFTER_TRANS) {
|
||||
enteringView.state = STATE_ACTIVE;
|
||||
}
|
||||
if (leavingView.state !== STATE_REMOVE_AFTER_TRANS) {
|
||||
leavingView.state = STATE_INACTIVE;
|
||||
}
|
||||
|
||||
if (leavingView.state !== STATE_REMOVE_AFTER_TRANS) {
|
||||
leavingView.state = STATE_INACTIVE;
|
||||
}
|
||||
// only need to do all this clean up if the transition
|
||||
// completed, otherwise nothing actually changed
|
||||
// destroy all of the views that come after the active view
|
||||
this._cleanup();
|
||||
|
||||
// destroy all of the views that come after the active view
|
||||
this._cleanup();
|
||||
// make sure only this entering view and PREVIOUS view are the
|
||||
// only two views that are not display:none
|
||||
leavingView = this.getPrevious(enteringView);
|
||||
this._views.forEach(view => {
|
||||
let shouldShow = (view === enteringView) || (view === leavingView);
|
||||
view.domCache(shouldShow, this._renderer);
|
||||
});
|
||||
|
||||
// make sure only this entering view and PREVIOUS view are the
|
||||
// only two views that are not display:none
|
||||
leavingView = this.getPrevious(enteringView);
|
||||
this._views.forEach(view => {
|
||||
let shouldShow = (view === enteringView) || (view === leavingView);
|
||||
view.domCache(shouldShow, this._renderer);
|
||||
});
|
||||
// this check only needs to happen once, which will add the css
|
||||
// class to the nav when it's finished its first transition
|
||||
if (!this._init) {
|
||||
this._init = true;
|
||||
this._renderer.setElementClass(this.elementRef.nativeElement, 'has-views', true);
|
||||
}
|
||||
|
||||
// this check only needs to happen once, which will add the css
|
||||
// class to the nav when it's finished its first transition
|
||||
if (!this._init) {
|
||||
this._init = true;
|
||||
this._renderer.setElementClass(this.elementRef.nativeElement, 'has-views', true);
|
||||
} else {
|
||||
// this transition has not completed, meaning the
|
||||
// entering view did not end up as the active view
|
||||
// this would happen when swipe to go back started
|
||||
// but the user did not complete the swipe and the
|
||||
// what was the active view stayed as the active view
|
||||
leavingView.state = STATE_ACTIVE;
|
||||
enteringView.state = STATE_INACTIVE;
|
||||
}
|
||||
|
||||
// allow clicks and enable the app again
|
||||
this._app && this._app.setEnabled(true);
|
||||
this.setTransitioning(false);
|
||||
|
||||
if (this.router && direction !== null) {
|
||||
if (this.router && direction !== null && hasCompleted) {
|
||||
// notify router of the state change if a direction was provided
|
||||
this.router.stateChange(direction, enteringView);
|
||||
}
|
||||
|
||||
// see if we should add the swipe back gesture listeners or not
|
||||
this._sbCheck();
|
||||
|
||||
} else {
|
||||
// darn, so this wasn't the most recent transition
|
||||
// so while this one did end, there's another more recent one
|
||||
@ -1189,6 +1205,7 @@ export class NavController extends Ion {
|
||||
this._views.splice(this.indexOf(view), 1);
|
||||
view.destroy();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1269,156 +1286,100 @@ export class NavController extends Ion {
|
||||
* @private
|
||||
*/
|
||||
swipeBackStart() {
|
||||
return;
|
||||
if (!this._app.isEnabled() || !this.canSwipeBack()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// disables the app during the transition
|
||||
this._app.setEnabled(false);
|
||||
this.setTransitioning(true);
|
||||
|
||||
// default the direction to "back"
|
||||
let opts = {
|
||||
direction: 'back'
|
||||
let opts: NavOptions = {
|
||||
direction: 'back',
|
||||
progressAnimation: true
|
||||
};
|
||||
|
||||
// get the active view and set that it is staged to be leaving
|
||||
// was probably the one popped from the stack
|
||||
let leavingView = this.getActive() || new ViewController();
|
||||
leavingView.willLeave();
|
||||
leavingView.willUnload();
|
||||
// figure out the states of each view in the stack
|
||||
let leavingView = this._remove(this._views.length - 1, 1);
|
||||
|
||||
// the entering view is now the new last view
|
||||
let enteringView = this.getPrevious(leavingView);
|
||||
enteringView.willEnter();
|
||||
if (leavingView) {
|
||||
opts.animation = leavingView.getTransitionName(opts.direction);
|
||||
|
||||
// wait for the new view to complete setup
|
||||
this._render(0, enteringView, leavingView, {}, () => {
|
||||
// get the view thats ready to enter
|
||||
let enteringView = this.getByState(STATE_INIT_ENTER);
|
||||
|
||||
|
||||
});
|
||||
// start the transition, fire callback when done...
|
||||
this._transition(enteringView, leavingView, opts, (hasCompleted: boolean) => {
|
||||
// swipe back has finished!!
|
||||
console.debug('swipeBack, hasCompleted', hasCompleted);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
swipeBackProgress(value) {
|
||||
return;
|
||||
if (this._sbTrans) {
|
||||
swipeBackProgress(stepValue: number) {
|
||||
if (this._trans && this._sbGesture) {
|
||||
// continue to disable the app while actively dragging
|
||||
this._app.setEnabled(false, 4000);
|
||||
this.setTransitioning(true, 4000);
|
||||
|
||||
// set the transition animation's progress
|
||||
this._sbTrans.progressStep(value);
|
||||
this._trans.progressStep(stepValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
swipeBackEnd(completeSwipeBack, rate) {
|
||||
return;
|
||||
if (!this._sbTrans) return;
|
||||
|
||||
// disables the app during the transition
|
||||
this._app.setEnabled(false);
|
||||
this.setTransitioning(true);
|
||||
|
||||
this._sbTrans.onFinish(() => {
|
||||
this._zone.run(() => {
|
||||
// find the views that were entering and leaving
|
||||
let enteringView = null;// this._getStagedEntering();
|
||||
let leavingView = null;//this._getStagedLeaving();
|
||||
|
||||
if (enteringView && leavingView) {
|
||||
// finish up the animation
|
||||
|
||||
if (completeSwipeBack) {
|
||||
// swipe back has completed navigating back
|
||||
// update each view's state
|
||||
enteringView.state = STATE_ACTIVE;
|
||||
leavingView.state = STATE_INACTIVE;
|
||||
|
||||
enteringView.didEnter();
|
||||
leavingView.didLeave();
|
||||
|
||||
if (this.router) {
|
||||
// notify router of the pop state change
|
||||
this.router.stateChange('pop', enteringView);
|
||||
}
|
||||
|
||||
} else {
|
||||
// cancelled the swipe back, they didn't end up going back
|
||||
// return views to their original state
|
||||
leavingView.state = STATE_ACTIVE;
|
||||
enteringView.state = STATE_INACTIVE;
|
||||
|
||||
leavingView.willEnter();
|
||||
leavingView.didEnter();
|
||||
enteringView.didLeave();
|
||||
|
||||
leavingView.shouldDestroy = false;
|
||||
enteringView.shouldDestroy = false;
|
||||
}
|
||||
}
|
||||
|
||||
// empty out and dispose the swipe back transition animation
|
||||
this._sbTrans && this._sbTrans.destroy();
|
||||
this._sbTrans = null;
|
||||
|
||||
// all done!
|
||||
//this._transComplete();
|
||||
});
|
||||
}, true);
|
||||
|
||||
this._sbTrans.progressEnd(completeSwipeBack, 0.5);
|
||||
swipeBackEnd(shouldComplete: boolean, currentStepValue: number) {
|
||||
if (this._trans && this._sbGesture) {
|
||||
// the swipe back gesture has ended
|
||||
this._trans.progressEnd(shouldComplete, currentStepValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
private _sbComplete() {
|
||||
return;
|
||||
if (this.canSwipeBack()) {
|
||||
// it is possible to swipe back
|
||||
private _sbCheck() {
|
||||
if (this._sbEnabled) {
|
||||
// this nav controller can have swipe to go back
|
||||
|
||||
if (this.sbGesture) {
|
||||
// this is already an active gesture, don't create another one
|
||||
return;
|
||||
if (!this._sbGesture) {
|
||||
// create the swipe back gesture if we haven't already
|
||||
let opts = {
|
||||
edge: 'left',
|
||||
threshold: this._sbThreshold
|
||||
};
|
||||
this._sbGesture = new SwipeBackGesture(this.getNativeElement(), opts, this);
|
||||
}
|
||||
|
||||
let opts = {
|
||||
edge: 'left',
|
||||
threshold: this._sbThreshold
|
||||
};
|
||||
this.sbGesture = new SwipeBackGesture(this.getNativeElement(), opts, this);
|
||||
console.debug('SwipeBackGesture listen');
|
||||
this.sbGesture.listen();
|
||||
if (this.canSwipeBack()) {
|
||||
// it is be possible to swipe back
|
||||
if (!this._sbGesture.isListening) {
|
||||
this._zone.runOutsideAngular(() => {
|
||||
// start listening if it's not already
|
||||
console.debug('swipeBack gesture, listen');
|
||||
this._sbGesture.listen();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
} else if (this.sbGesture) {
|
||||
// it is not possible to swipe back and there is an
|
||||
// active sbGesture, so unlisten it
|
||||
console.debug('SwipeBackGesture unlisten');
|
||||
this.sbGesture.unlisten();
|
||||
this.sbGesture = null;
|
||||
} else if (this._sbGesture.isListening) {
|
||||
// it should not be possible to swipe back
|
||||
// but the gesture is still listening
|
||||
console.debug('swipeBack gesture, unlisten');
|
||||
this._sbGesture.unlisten();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if swipe-to-go-back is enabled
|
||||
* @param {boolean} isSwipeBackEnabled Set whether or not swipe-to-go-back is enabled
|
||||
* @returns {boolean} Whether swipe-to-go-back is enabled
|
||||
* @input {boolean} Whether it's possible to swipe-to-go-back on this nav controller or not.
|
||||
*/
|
||||
isSwipeBackEnabled(val?: boolean): boolean {
|
||||
if (arguments.length) {
|
||||
this._sbEnabled = !!val;
|
||||
}
|
||||
@Input()
|
||||
get swipeBackEnabled(): boolean {
|
||||
return this._sbEnabled;
|
||||
}
|
||||
|
||||
set swipeBackEnabled(val: boolean) {
|
||||
this._sbEnabled = isTrueProperty(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* If it's possible to use swipe back or not. If it's not possible
|
||||
* to go back, or swipe back is not enable then this will return false.
|
||||
@ -1427,7 +1388,7 @@ export class NavController extends Ion {
|
||||
* @returns {boolean} Whether you can swipe to go back
|
||||
*/
|
||||
canSwipeBack(): boolean {
|
||||
return (this._sbEnabled && this.canGoBack());
|
||||
return (this._sbEnabled && !this.isTransitioning() && this._app.isEnabled() && this.canGoBack());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1594,7 +1555,8 @@ export interface NavOptions {
|
||||
keyboardClose?: boolean;
|
||||
preload?: boolean;
|
||||
transitionDelay?: number;
|
||||
postLoad?: Function
|
||||
postLoad?: Function;
|
||||
progressAnimation?: boolean;
|
||||
}
|
||||
|
||||
const STATE_ACTIVE = 'active';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {Component, ElementRef, Input, Optional, NgZone, Compiler, AppViewManager, Renderer, Type, ViewChild} from 'angular2/core';
|
||||
import {Component, ElementRef, Input, Optional, NgZone, Compiler, AppViewManager, Renderer, Type} from 'angular2/core';
|
||||
|
||||
import {IonicApp} from '../app/app';
|
||||
import {Config} from '../../config/config';
|
||||
@ -98,7 +98,7 @@ import {ViewController} from './view-controller';
|
||||
* </pre>
|
||||
* </div>
|
||||
*
|
||||
* @demo /docs/v2/demos/navigation/
|
||||
* @demo /docs/v2/demos/navigation/
|
||||
* @see {@link /docs/v2/components#navigation Navigation Component Docs}
|
||||
*/
|
||||
@Component({
|
||||
@ -112,11 +112,6 @@ export class Nav extends NavController {
|
||||
*/
|
||||
@Input() root: Type;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Input() swipeBackEnabled: any;
|
||||
|
||||
constructor(
|
||||
@Optional() hostNavCtrl: NavController,
|
||||
@Optional() viewCtrl: ViewController,
|
||||
@ -149,9 +144,6 @@ export class Nav extends NavController {
|
||||
}
|
||||
this.push(this.root);
|
||||
}
|
||||
|
||||
// default the swipe back to be enabled
|
||||
this.isSwipeBackEnabled( (this.swipeBackEnabled || '').toString() !== 'false' );
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,36 +1,57 @@
|
||||
import {NavController} from './nav-controller';
|
||||
import {SlideEdgeGesture} from '../../gestures/slide-edge-gesture';
|
||||
import {SlideData} from '../../gestures/slide-gesture';
|
||||
import {assign} from '../../util/util';
|
||||
|
||||
|
||||
export class SwipeBackGesture extends SlideEdgeGesture {
|
||||
public edges: Array<string>;
|
||||
public threshold: string;
|
||||
|
||||
constructor(
|
||||
element: HTMLElement,
|
||||
opts: any = {},
|
||||
element: HTMLElement,
|
||||
options: any,
|
||||
private _nav: NavController
|
||||
) {
|
||||
super(element, opts);
|
||||
|
||||
// Can check corners through use of eg 'left top'
|
||||
this.edges = opts.edge.split(' ');
|
||||
this.threshold = opts.threshold;
|
||||
super(element, assign({
|
||||
direction: 'x',
|
||||
maxEdgeStart: 75
|
||||
}, options));
|
||||
}
|
||||
|
||||
onSlideStart() {
|
||||
canStart(ev: any) {
|
||||
// the gesture swipe angle must be mainly horizontal and the
|
||||
// gesture distance would be relatively short for a swipe back
|
||||
// and swipe back must be possible on this nav controller
|
||||
if (ev.angle > -40 &&
|
||||
ev.angle < 40 &&
|
||||
ev.distance < 50 &&
|
||||
this._nav.canSwipeBack()) {
|
||||
// passed the tests, now see if the super says it's cool or not
|
||||
return super.canStart(ev);
|
||||
}
|
||||
|
||||
// nerp, not today
|
||||
return false;
|
||||
}
|
||||
|
||||
onSlideBeforeStart() {
|
||||
console.debug('swipeBack, onSlideBeforeStart');
|
||||
this._nav.swipeBackStart();
|
||||
}
|
||||
|
||||
onSlide(slide, ev) {
|
||||
this._nav.swipeBackProgress(slide.distance / slide.max);
|
||||
onSlide(slide: SlideData) {
|
||||
let stepValue = (slide.distance / slide.max);
|
||||
console.debug('swipeBack, onSlide, distance', slide.distance, 'max', slide.max, 'stepValue', stepValue);
|
||||
this._nav.swipeBackProgress(stepValue);
|
||||
}
|
||||
|
||||
onSlideEnd(slide, ev) {
|
||||
onSlideEnd(slide: SlideData, ev: any) {
|
||||
let shouldComplete = (Math.abs(ev.velocityX) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5);
|
||||
|
||||
// TODO: calculate a better playback rate depending on velocity and distance
|
||||
this._nav.swipeBackEnd(shouldComplete, 1);
|
||||
let currentStepValue = (slide.distance / slide.max);
|
||||
|
||||
console.debug('swipeBack, onSlideEnd, shouldComplete', shouldComplete, 'currentStepValue', currentStepValue);
|
||||
|
||||
this._nav.swipeBackEnd(shouldComplete, currentStepValue);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ export function run() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('_transComplete', () => {
|
||||
describe('_transFinish', () => {
|
||||
|
||||
it('should not entering/leaving state, after transition that isnt the most recent, and state already changed', () => {
|
||||
let enteringView = new ViewController(Page1);
|
||||
@ -301,7 +301,7 @@ export function run() {
|
||||
|
||||
nav._transIds = 2;
|
||||
|
||||
nav._transComplete(1, enteringView, leavingView);
|
||||
nav._transFinish(1, enteringView, leavingView, 'forward', true);
|
||||
|
||||
expect(enteringView.state).toBe('somethingelse');
|
||||
expect(leavingView.state).toBe('somethingelse');
|
||||
@ -315,7 +315,7 @@ export function run() {
|
||||
|
||||
nav._transIds = 2;
|
||||
|
||||
nav._transComplete(1, enteringView, leavingView);
|
||||
nav._transFinish(1, enteringView, leavingView, 'forward', true);
|
||||
|
||||
expect(enteringView.state).toBe(STATE_INACTIVE);
|
||||
expect(leavingView.state).toBe(STATE_INACTIVE);
|
||||
@ -329,12 +329,26 @@ export function run() {
|
||||
|
||||
nav._transIds = 1;
|
||||
|
||||
nav._transComplete(1, enteringView, leavingView);
|
||||
nav._transFinish(1, enteringView, leavingView, 'forward', true);
|
||||
|
||||
expect(enteringView.state).toBe(STATE_ACTIVE);
|
||||
expect(leavingView.state).toBe(STATE_INACTIVE);
|
||||
});
|
||||
|
||||
it('should set entering inactive, leaving active, after transition has not completed', () => {
|
||||
let enteringView = new ViewController(Page1);
|
||||
enteringView.state = STATE_TRANS_ENTER;
|
||||
let leavingView = new ViewController(Page2);
|
||||
leavingView.state = STATE_TRANS_LEAVE;
|
||||
|
||||
nav._transIds = 1;
|
||||
|
||||
nav._transFinish(1, enteringView, leavingView, 'back', false);
|
||||
|
||||
expect(enteringView.state).toBe(STATE_INACTIVE);
|
||||
expect(leavingView.state).toBe(STATE_ACTIVE);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('_insert', () => {
|
||||
@ -541,7 +555,7 @@ export function run() {
|
||||
});
|
||||
|
||||
it('should getByState()', () => {
|
||||
expect(nav.getByState()).toBe(null);
|
||||
expect(nav.getByState(null)).toBe(null);
|
||||
|
||||
let view1 = new ViewController(Page1);
|
||||
view1.state = STATE_INIT_ENTER;
|
||||
@ -618,7 +632,7 @@ export function run() {
|
||||
});
|
||||
|
||||
// setup stuff
|
||||
let nav;
|
||||
let nav: NavController;
|
||||
let config = new Config();
|
||||
|
||||
class Page1 {}
|
||||
|
@ -48,21 +48,23 @@ export class Gesture {
|
||||
}
|
||||
|
||||
listen() {
|
||||
this._hammer = Hammer(this.element, this._options);
|
||||
if (!this.isListening) {
|
||||
this._hammer = Hammer(this.element, this._options);
|
||||
}
|
||||
this.isListening = true;
|
||||
}
|
||||
|
||||
unlisten() {
|
||||
var type, i;
|
||||
if (this._hammer) {
|
||||
if (this._hammer && this.isListening) {
|
||||
for (type in this._callbacks) {
|
||||
for (i = 0; i < this._callbacks[type].length; i++) {
|
||||
this._hammer.off(type, this._callbacks[type]);
|
||||
}
|
||||
}
|
||||
this._callbacks = {};
|
||||
this._hammer.destroy();
|
||||
}
|
||||
this._callbacks = {};
|
||||
this.isListening = false;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user