From e46863640eb3793cc40aa75e1df899eb07c3664d Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Wed, 9 Sep 2015 10:03:48 -0500 Subject: [PATCH] do not deactivate buttons during transition Closes #94 --- ionic/components/app/app.ts | 19 ++++++ ionic/components/overlay/overlay.ts | 4 ++ ionic/components/text-input/text-input.ts | 2 + ionic/components/view/view-controller.ts | 3 + ionic/util/activator.ts | 71 +++++++++++------------ 5 files changed, 62 insertions(+), 37 deletions(-) diff --git a/ionic/components/app/app.ts b/ionic/components/app/app.ts index 388b152c5a..b47b89dc3b 100644 --- a/ionic/components/app/app.ts +++ b/ionic/components/app/app.ts @@ -39,6 +39,7 @@ export class IonicApp { */ constructor() { this.overlays = []; + this._isTransitioning = false; // Our component registry map this.components = {}; @@ -74,6 +75,24 @@ export class IonicApp { document.title = val; } + /** + * Sets if the app is currently transitioning or not. For example + * this is set to `true` while views transition, a modal slides up, an action-menu + * slides up, etc. After the transition completes it is set back to `false`. + * @param {bool} isTransitioning + */ + setTransitioning(isTransitioning) { + this._isTransitioning = !!isTransitioning; + } + + /** + * Boolean if the app is actively transitioning or not. + * @return {bool} + */ + isTransitioning() { + return this._isTransitioning; + } + /** * TODO * @param {TODO=} val TODO diff --git a/ionic/components/overlay/overlay.ts b/ionic/components/overlay/overlay.ts index c94fe57e24..019d665b9f 100644 --- a/ionic/components/overlay/overlay.ts +++ b/ionic/components/overlay/overlay.ts @@ -128,6 +128,7 @@ export class OverlayRef { animation.before.addClass('show-overlay'); ClickBlock(true, animation.duration() + 200); + this.app.setTransitioning(true); this.app.zoneRunOutside(() => { @@ -135,6 +136,7 @@ export class OverlayRef { this.app.zoneRun(() => { ClickBlock(false); + this.app.setTransitioning(false); animation.dispose(); instance.viewDidEnter && instance.viewDidEnter(); resolve(); @@ -160,6 +162,7 @@ export class OverlayRef { animation.after.removeClass('show-overlay'); ClickBlock(true, animation.duration() + 200); + this.app.setTransitioning(true); animation.play().then(() => { instance.viewDidLeave && instance.viewDidLeave(); @@ -168,6 +171,7 @@ export class OverlayRef { this._dispose(); ClickBlock(false); + this.app.setTransitioning(false); animation.dispose(); resolve(); diff --git a/ionic/components/text-input/text-input.ts b/ionic/components/text-input/text-input.ts index fa011eb993..796e0a3c43 100644 --- a/ionic/components/text-input/text-input.ts +++ b/ionic/components/text-input/text-input.ts @@ -228,6 +228,7 @@ export class TextInput extends Ion { // manually scroll the text input to the top // do not allow any clicks while it's scrolling ClickBlock(true, SCROLL_INTO_VIEW_DURATION + 100); + this.app.setTransitioning(true); // temporarily move the focus to the focus holder so the browser // doesn't freak out while it's trying to get the input in place @@ -242,6 +243,7 @@ export class TextInput extends Ion { // all good, allow clicks again ClickBlock(false); + this.app.setTransitioning(false); }); } else { diff --git a/ionic/components/view/view-controller.ts b/ionic/components/view/view-controller.ts index ee6952cbda..5f024c3994 100644 --- a/ionic/components/view/view-controller.ts +++ b/ionic/components/view/view-controller.ts @@ -279,6 +279,7 @@ export class ViewController extends Ion { // block any clicks during the transition and provide a // fallback to remove the clickblock if something goes wrong ClickBlock(true, duration + 200); + this.app.setTransitioning(true); } // start the transition @@ -396,6 +397,7 @@ export class ViewController extends Ion { swipeBackProgress(progress) { if (this.sbTransition) { ClickBlock(true, 4000); + this.app.setTransitioning(true); this.sbTransition.progress( Math.min(1, Math.max(0, progress)) ); } } @@ -481,6 +483,7 @@ export class ViewController extends Ion { // allow clicks again ClickBlock(false); + this.app.setTransitioning(false); } /** diff --git a/ionic/util/activator.ts b/ionic/util/activator.ts index 2adb03c28c..16491e5b40 100644 --- a/ionic/util/activator.ts +++ b/ionic/util/activator.ts @@ -15,6 +15,7 @@ export class Activator { self.active = {}; self.activatedClass = 'activated'; self.deactivateTimeout = 180; + this.deactivateAttempt = 0; self.pointerTolerance = 4; self.isTouch = false; self.disableClick = 0; @@ -109,7 +110,8 @@ export class Activator { mouseDown(ev) { if (this.isDisabledClick()) { console.debug('mouseDown prevent'); - preventEvent(ev); + ev.preventDefault(); + ev.stopPropagation(); } else if (!self.isTouch) { this.pointerStart(ev); @@ -123,7 +125,8 @@ export class Activator { mouseUp(ev) { if (this.isDisabledClick()) { console.debug('mouseUp prevent'); - preventEvent(ev); + ev.preventDefault(); + ev.stopPropagation(); } if (!self.isTouch) { @@ -153,7 +156,7 @@ export class Activator { * TODO */ pointerEnd(ev) { - this.endActive(); + this.queueDeactivate(); this.moveListeners(false); } @@ -162,7 +165,7 @@ export class Activator { */ pointerCancel() { console.debug('pointerCancel') - this.clearActive(); + this.deactivate(); this.moveListeners(false); this.disableClick = Date.now(); } @@ -192,7 +195,8 @@ export class Activator { click(ev) { if (!this.allowClick(ev)) { console.debug('click prevent'); - preventEvent(ev); + ev.preventDefault(); + ev.stopPropagation(); } this.isTouch = false; } @@ -240,50 +244,43 @@ export class Activator { }); } - endActive() { + queueDeactivate() { const self = this; setTimeout(function() { - self.clearActive(); + self.deactivate(); }, this.deactivateTimeout); } - clearActive() { + deactivate() { const self = this; - // clear out any elements that are queued to be set to active - self.queue = {}; + if (this.app.isTransitioning() && this.deactivateAttempt < 10) { + // the app is actively transitioning, don't bother deactivating + // anything this makes it easier on the GPU so it doesn't + // have to redraw any buttons during a transition + // retry + ++this.deactivateAttempt; + this.queueDeactivate(); - // in the next frame, remove the active class from all active elements - raf(function() { - for (var key in self.active) { - if (self.active[key]) { - self.active[key].classList.remove(self.activatedClass); + } else { + // not actively transitioning, good to deactivate any elements + // clear out any elements that are queued to be set to active + self.queue = {}; + + // in the next frame, remove the active class from all active elements + raf(function() { + for (var key in self.active) { + if (self.active[key]) { + self.active[key].classList.remove(self.activatedClass); + } + delete self.active[key]; } - delete self.active[key]; - } - }); - } + }); - clickBlock(enable) { - console.log('clickBlock', enable); - - this.doc.removeEventListener('click', preventEvent, true); - this.doc.removeEventListener('touchmove', preventEvent, true); - this.doc.removeEventListener('touchstart', preventEvent, true); - this.doc.removeEventListener('touchend', preventEvent, true); - - if (enable) { - this.doc.addEventListener('click', preventEvent, true); - this.doc.addEventListener('touchmove', preventEvent, true); - this.doc.addEventListener('touchstart', preventEvent, true); - this.doc.addEventListener('touchend', preventEvent, true); + this.deactivateAttempt = 0; } + } } - -function preventEvent(ev) { - ev.preventDefault(); - ev.stopPropagation(); -}