fix(animation): improve menu and go back swipe

This commit is contained in:
Manu Mtz.-Almeida
2016-11-21 23:09:57 +01:00
parent 3b304974ec
commit 4be47bd3dd
8 changed files with 49 additions and 37 deletions

View File

@ -13,6 +13,7 @@ export class Animation {
private _fx: EffectProperty[]; private _fx: EffectProperty[];
private _dur: number = null; private _dur: number = null;
private _es: string = null; private _es: string = null;
private _rvEs: string = null;
private _bfSty: { [property: string]: any; }; private _bfSty: { [property: string]: any; };
private _bfAdd: string[]; private _bfAdd: string[];
private _bfRm: string[]; private _bfRm: string[];
@ -114,6 +115,9 @@ export class Animation {
* not have an easing, then it'll get the easing from its parent. * not have an easing, then it'll get the easing from its parent.
*/ */
getEasing(): string { getEasing(): string {
if (this._rv && this._rvEs) {
return this._rvEs;
}
return this._es !== null ? this._es : (this.parent && this.parent.getEasing()) || null; return this._es !== null ? this._es : (this.parent && this.parent.getEasing()) || null;
} }
@ -125,6 +129,14 @@ export class Animation {
return this; return this;
} }
/**
* Set the easing for this reversed animation.
*/
easingReverse(name: string): Animation {
this._rvEs = name;
return this;
}
/** /**
* Add the "from" value for a specific property. * Add the "from" value for a specific property.
*/ */
@ -996,14 +1008,15 @@ export class Animation {
/** /**
* End the progress animation. * End the progress animation.
*/ */
progressEnd(shouldComplete: boolean, currentStepValue: number, maxDelta: number = 0) { progressEnd(shouldComplete: boolean, currentStepValue: number, dur: number = -1) {
console.debug('Animation, progressEnd, shouldComplete', shouldComplete, 'currentStepValue', currentStepValue); console.debug('Animation, progressEnd, shouldComplete', shouldComplete, 'currentStepValue', currentStepValue);
this._isAsync = (currentStepValue > 0.05 && currentStepValue < 0.95);
const stepValue = shouldComplete ? 1 : 0; const stepValue = shouldComplete ? 1 : 0;
const factor = Math.max(Math.abs(currentStepValue - stepValue), 0.5) * 2;
const dur = 64 + factor * maxDelta; if (dur < 0) {
dur = this._dur;
}
this._isAsync = (currentStepValue > 0.05 && currentStepValue < 0.95 && dur > 30);
this._progressEnd(shouldComplete, stepValue, dur, this._isAsync); this._progressEnd(shouldComplete, stepValue, dur, this._isAsync);

View File

@ -78,7 +78,7 @@ export class MenuContentGesture extends SlideEdgeGesture {
'shouldCompleteRight', shouldCompleteRight, 'shouldCompleteRight', shouldCompleteRight,
'currentStepValue', currentStepValue); 'currentStepValue', currentStepValue);
this.menu.swipeEnd(shouldCompleteLeft, shouldCompleteRight, currentStepValue); this.menu.swipeEnd(shouldCompleteLeft, shouldCompleteRight, currentStepValue, velocity);
} }
getElementStartPos(slide: SlideData, ev: any) { getElementStartPos(slide: SlideData, ev: any) {

View File

@ -15,10 +15,18 @@ export class MenuType {
ani: Animation = new Animation(); ani: Animation = new Animation();
isOpening: boolean; isOpening: boolean;
constructor() {
this.ani
.easing('cubic-bezier(0.0, 0.0, 0.2, 1)')
.easingReverse('cubic-bezier(0.4, 0.0, 0.6, 1)')
.duration(280);
}
setOpen(shouldOpen: boolean, animated: boolean, done: Function) { setOpen(shouldOpen: boolean, animated: boolean, done: Function) {
let ani = this.ani let ani = this.ani
.onFinish(done, true) .onFinish(done, true)
.reverse(!shouldOpen); .reverse(!shouldOpen);
if (animated) { if (animated) {
ani.play(); ani.play();
} else { } else {
@ -40,7 +48,7 @@ export class MenuType {
this.ani.progressStep(stepValue); this.ani.progressStep(stepValue);
} }
setProgressEnd(shouldComplete: boolean, currentStepValue: number, done: Function) { setProgressEnd(shouldComplete: boolean, currentStepValue: number, velocity: number, done: Function) {
let isOpen = (this.isOpening && shouldComplete); let isOpen = (this.isOpening && shouldComplete);
if (!this.isOpening && !shouldComplete) { if (!this.isOpening && !shouldComplete) {
isOpen = true; isOpen = true;
@ -51,7 +59,9 @@ export class MenuType {
done(isOpen); done(isOpen);
}, true); }, true);
this.ani.progressEnd(shouldComplete, currentStepValue); let dur = this.ani.getDuration() / (Math.abs(velocity) + 1);
this.ani.progressEnd(shouldComplete, currentStepValue, dur);
} }
destroy() { destroy() {
@ -72,11 +82,6 @@ class MenuRevealType extends MenuType {
super(); super();
let openedX = (menu.width() * (menu.side === 'right' ? -1 : 1)) + 'px'; let openedX = (menu.width() * (menu.side === 'right' ? -1 : 1)) + 'px';
this.ani
.easing('ease')
.duration(250);
let contentOpen = new Animation(menu.getContentElement()); let contentOpen = new Animation(menu.getContentElement());
contentOpen.fromTo('translateX', '0px', openedX); contentOpen.fromTo('translateX', '0px', openedX);
this.ani.add(contentOpen); this.ani.add(contentOpen);
@ -95,10 +100,6 @@ class MenuPushType extends MenuType {
constructor(menu: Menu, platform: Platform) { constructor(menu: Menu, platform: Platform) {
super(); super();
this.ani
.easing('ease')
.duration(250);
let contentOpenedX: string, menuClosedX: string, menuOpenedX: string; let contentOpenedX: string, menuClosedX: string, menuOpenedX: string;
if (menu.side === 'right') { if (menu.side === 'right') {
@ -135,10 +136,6 @@ class MenuOverlayType extends MenuType {
constructor(menu: Menu, platform: Platform) { constructor(menu: Menu, platform: Platform) {
super(); super();
this.ani
.easing('ease')
.duration(250);
let closedX: string, openedX: string; let closedX: string, openedX: string;
if (menu.side === 'right') { if (menu.side === 'right') {
// right side // right side

View File

@ -58,6 +58,12 @@ ion-menu ion-backdrop {
transform: translate3d(0, 0, 0); transform: translate3d(0, 0, 0);
} }
.menu-content-open {
cursor: pointer;
touch-action: manipulation;
}
.menu-content-open ion-pane, .menu-content-open ion-pane,
.menu-content-open ion-content, .menu-content-open ion-content,
.menu-content-open .toolbar { .menu-content-open .toolbar {

View File

@ -465,7 +465,7 @@ export class Menu {
/** /**
* @private * @private
*/ */
swipeEnd(shouldCompleteLeft: boolean, shouldCompleteRight: boolean, stepValue: number) { swipeEnd(shouldCompleteLeft: boolean, shouldCompleteRight: boolean, stepValue: number, velocity: number) {
if (!this._isAnimating) { if (!this._isAnimating) {
return; return;
} }
@ -478,7 +478,7 @@ export class Menu {
shouldComplete = (this.side === 'right') ? shouldCompleteRight : shouldCompleteLeft; shouldComplete = (this.side === 'right') ? shouldCompleteRight : shouldCompleteLeft;
} }
this._getType().setProgressEnd(shouldComplete, stepValue, (isOpen: boolean) => { this._getType().setProgressEnd(shouldComplete, stepValue, velocity, (isOpen: boolean) => {
console.debug('menu, swipeEnd', this.side); console.debug('menu, swipeEnd', this.side);
this._after(isOpen); this._after(isOpen);
}); });
@ -512,14 +512,9 @@ export class Menu {
this._cntEle.classList.add('menu-content-open'); this._cntEle.classList.add('menu-content-open');
let callback = this.onBackdropClick.bind(this); let callback = this.onBackdropClick.bind(this);
this._events.pointerEvents({ this._events.listen(this._cntEle, 'click', callback, true);
element: this._cntEle, this._events.listen(this.backdrop.getNativeElement(), 'click', callback, true);
pointerDown: callback
});
this._events.pointerEvents({
element: this.backdrop.getNativeElement(),
pointerDown: callback
});
this.ionOpen.emit(true); this.ionOpen.emit(true);
} else { } else {

View File

@ -65,7 +65,7 @@ export class NavControllerBase extends Ion implements NavController {
super(config, elementRef, renderer); super(config, elementRef, renderer);
this._sbEnabled = config.getBoolean('swipeBackEnabled'); this._sbEnabled = config.getBoolean('swipeBackEnabled');
this._sbThreshold = config.getNumber('swipeBackThreshold', 40); this._sbThreshold = config.getNumber('swipeBackThreshold', 0);
this.id = 'n' + (++ctrlIds); this.id = 'n' + (++ctrlIds);
} }
@ -918,10 +918,11 @@ export class NavControllerBase extends Ion implements NavController {
} }
} }
swipeBackEnd(shouldComplete: boolean, currentStepValue: number) { swipeBackEnd(shouldComplete: boolean, currentStepValue: number, velocity: number) {
if (this._sbTrns && this._sbGesture) { if (this._sbTrns && this._sbGesture) {
// the swipe back gesture has ended // the swipe back gesture has ended
this._sbTrns.progressEnd(shouldComplete, currentStepValue, 300); const dur = this._sbTrns.getDuration() / (Math.abs(velocity) + 1);
this._sbTrns.progressEnd(shouldComplete, currentStepValue, dur);
} }
} }

View File

@ -53,12 +53,13 @@ export class SwipeBackGesture extends SlideEdgeGesture {
} }
onSlideEnd(slide: SlideData, ev: any) { onSlideEnd(slide: SlideData, ev: any) {
const velocity = slide.velocity;
const currentStepValue = (slide.distance / slide.max); const currentStepValue = (slide.distance / slide.max);
const isResetDirecction = slide.velocity < 0; const isResetDirecction = velocity < 0;
const isMovingFast = Math.abs(slide.velocity) > 0.4; const isMovingFast = Math.abs(slide.velocity) > 0.4;
const isInResetZone = Math.abs(slide.delta) < Math.abs(slide.max) * 0.5; const isInResetZone = Math.abs(slide.delta) < Math.abs(slide.max) * 0.5;
const shouldComplete = !swipeShouldReset(isResetDirecction, isMovingFast, isInResetZone); const shouldComplete = !swipeShouldReset(isResetDirecction, isMovingFast, isInResetZone);
this._nav.swipeBackEnd(shouldComplete, currentStepValue); this._nav.swipeBackEnd(shouldComplete, currentStepValue, velocity);
} }
} }

View File

@ -109,7 +109,6 @@ export const PLATFORM_CONFIGS: {[key: string]: PlatformConfig} = {
scrollAssist: isIOS, scrollAssist: isIOS,
statusbarPadding: isCordova, statusbarPadding: isCordova,
swipeBackEnabled: isIOS, swipeBackEnabled: isIOS,
swipeBackThreshold: 40,
tapPolyfill: isIOSUI, tapPolyfill: isIOSUI,
virtualScrollEventAssist: isIOSUI, virtualScrollEventAssist: isIOSUI,
disableScrollAssist: isIOS, disableScrollAssist: isIOS,