From 14b8049771622f99bc62671a7257d218055e4dad Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Tue, 21 Jun 2016 11:37:31 +0200 Subject: [PATCH 1/3] refactor(refresh): uses new UIEventManager --- src/components/content/content.ts | 7 +++ src/components/refresher/refresher.ts | 82 +++++---------------------- src/util/ui-event-manager.ts | 50 +++++++++++----- 3 files changed, 57 insertions(+), 82 deletions(-) diff --git a/src/components/content/content.ts b/src/components/content/content.ts index 0ab440e4c9..d11dd973ad 100644 --- a/src/components/content/content.ts +++ b/src/components/content/content.ts @@ -170,6 +170,13 @@ export class Content extends Ion { } }; } + + /** + * @private + */ + getScrollElement(): HTMLElement { + return this._scrollEle; + } /** * @private diff --git a/src/components/refresher/refresher.ts b/src/components/refresher/refresher.ts index 19afeef3a7..d580c9195b 100644 --- a/src/components/refresher/refresher.ts +++ b/src/components/refresher/refresher.ts @@ -4,6 +4,7 @@ import {Content} from '../content/content'; import {Icon} from '../icon/icon'; import {isTrueProperty} from '../../util/util'; import {CSS, pointerCoord, transitionEnd} from '../../util/dom'; +import {PointerEvents, UIEventManager} from '../../util/ui-event-manager'; /** @@ -95,15 +96,10 @@ import {CSS, pointerCoord, transitionEnd} from '../../util/dom'; export class Refresher { private _appliedStyles: boolean = false; private _didStart: boolean; - private _lastStart: number = 0; private _lastCheck: number = 0; private _isEnabled: boolean = true; - private _mDown: Function; - private _mMove: Function; - private _mUp: Function; - private _tStart: Function; - private _tMove: Function; - private _tEnd: Function; + private _events: UIEventManager = new UIEventManager(false); + private _pointerEvents: PointerEvents; /** * The current state which the refresher is in. The refresher's states include: @@ -202,8 +198,7 @@ export class Refresher { constructor( @Host() private _content: Content, private _zone: NgZone, - elementRef: ElementRef - ) { + elementRef: ElementRef) { _content.addCssClass('has-refresher'); // deprecated warning @@ -222,31 +217,22 @@ export class Refresher { private _onStart(ev: TouchEvent): any { // if multitouch then get out immediately if (ev.touches && ev.touches.length > 1) { - return 1; + return false; } let coord = pointerCoord(ev); console.debug('Pull-to-refresh, onStart', ev.type, 'y:', coord.y); - let now = Date.now(); - if (this._lastStart + 100 > now) { - return 2; - } - this._lastStart = now; - - if ( ev.type === 'mousedown' && !this._mMove) { - this._mMove = this._content.addMouseMoveListener( this._onMove.bind(this) ); - } - this.startY = this.currentY = coord.y; this.progress = 0; if (!this.pullMax) { this.pullMax = (this.pullMin + 60); } + return true; } - private _onMove(ev: TouchEvent): any { + private _onMove(ev: TouchEvent) { // this method can get called like a bazillion times per second, // so it's built to be as efficient as possible, and does its // best to do any DOM read/writes only when absolutely necessary @@ -396,12 +382,6 @@ export class Refresher { // reset on any touchend/mouseup this.startY = null; - if (this._mMove) { - // we don't want to always listen to mousemoves - // remove it if we're still listening - this._mMove(); - this._mMove = null; - } } private _beginRefresh() { @@ -463,10 +443,8 @@ export class Refresher { this.state = state; this._setCss(0, '', true, delay); - if (this._mMove) { - // always remove the mousemove event - this._mMove(); - this._mMove = null; + if (this._pointerEvents) { + this._pointerEvents.stop(); } } @@ -481,43 +459,13 @@ export class Refresher { } private _setListeners(shouldListen: boolean) { - const self = this; - const content = self._content; - + this._events.unlistenAll(); + this._pointerEvents = null; if (shouldListen) { - // add listener outside of zone - // touch handlers - self._zone.runOutsideAngular(function() { - if (!self._tStart) { - self._tStart = content.addTouchStartListener( self._onStart.bind(self) ); - } - if (!self._tMove) { - self._tMove = content.addTouchMoveListener( self._onMove.bind(self) ); - } - if (!self._tEnd) { - self._tEnd = content.addTouchEndListener( self._onEnd.bind(self) ); - } - - // mouse handlers - // mousemove does not get added until mousedown fires - if (!self._mDown) { - self._mDown = content.addMouseDownListener( self._onStart.bind(self) ); - } - if (!self._mUp) { - self._mUp = content.addMouseUpListener( self._onEnd.bind(self) ); - } - }); - - } else { - // unregister event listeners from content element - self._mDown && self._mDown(); - self._mMove && self._mMove(); - self._mUp && self._mUp(); - self._tStart && self._tStart(); - self._tMove && self._tMove(); - self._tEnd && self._tEnd(); - - self._mDown = self._mMove = self._mUp = self._tStart = self._tMove = self._tEnd = null; + this._pointerEvents = this._events.pointerEvents(this._content.getScrollElement(), + this._onStart.bind(this), + this._onMove.bind(this), + this._onEnd.bind(this)); } } diff --git a/src/util/ui-event-manager.ts b/src/util/ui-event-manager.ts index f85bab6819..9f886a92dc 100644 --- a/src/util/ui-event-manager.ts +++ b/src/util/ui-event-manager.ts @@ -1,9 +1,12 @@ import {ElementRef} from '@angular/core'; -const MOUSE_WAIT = 2 * 1000; -class PointerEvents { + +/** + * @private + */ +export class PointerEvents { private rmTouchStart: Function = null; private rmTouchMove: Function = null; private rmTouchEnd: Function = null; @@ -14,6 +17,8 @@ class PointerEvents { private lastTouchEvent: number = 0; + mouseWait: number = 2 * 1000; + constructor(private ele: any, private pointerDown: any, private pointerMove: any, @@ -26,7 +31,7 @@ class PointerEvents { } private handleTouchStart(ev: any) { - this.lastTouchEvent = Date.now() + MOUSE_WAIT; + this.lastTouchEvent = Date.now() + this.mouseWait; if (!this.pointerDown(ev)) { return; } @@ -72,22 +77,27 @@ class PointerEvents { this.pointerUp(ev); } - destroy() { - this.rmTouchStart && this.rmTouchStart(); + stop() { this.rmTouchMove && this.rmTouchMove(); this.rmTouchEnd && this.rmTouchEnd(); - - this.rmMouseStart && this.rmMouseStart(); - this.rmMouseMove && this.rmMouseMove(); - this.rmMouseUp && this.rmMouseUp(); - - this.rmTouchStart = null; this.rmTouchMove = null; this.rmTouchEnd = null; - this.rmMouseStart = null; + + this.rmMouseMove && this.rmMouseMove(); + this.rmMouseUp && this.rmMouseUp(); this.rmMouseMove = null; this.rmMouseUp = null; + } + destroy() { + this.rmTouchStart && this.rmTouchStart(); + this.rmTouchStart = null; + + this.rmMouseStart && this.rmMouseStart(); + this.rmMouseStart = null; + + this.stop(); + this.pointerDown = null; this.pointerMove = null; this.pointerUp = null; @@ -97,6 +107,10 @@ class PointerEvents { } + +/** + * @private + */ export class UIEventManager { private events: Function[] = []; @@ -109,8 +123,11 @@ export class UIEventManager { pointerEventsRef(ref: ElementRef, pointerStart: any, pointerMove: any, pointerEnd: any, option?: any): Function { return this.pointerEvents(ref.nativeElement, pointerStart, pointerMove, pointerEnd, option); } - - pointerEvents(element: any, pointerDown: any, pointerMove: any, pointerUp: any, option: any = false): Function { + + pointerEvents(element: any, pointerDown: any, pointerMove: any, pointerUp: any, option: any = false): PointerEvents { + if (!element) { + return; + } let submanager = new PointerEvents( element, pointerDown, @@ -121,10 +138,13 @@ export class UIEventManager { let removeFunc = () => submanager.destroy(); this.events.push(removeFunc); - return removeFunc; + return submanager; } listen(element: any, eventName: string, callback: any, option: any = false): Function { + if (!element) { + return; + } let removeFunc = listenEvent(element, eventName, this.zoneWrapped, option, callback); this.events.push(removeFunc); return removeFunc; From e3ecd3d05bff0e61da3354d47258e196ad3a6921 Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Tue, 21 Jun 2016 11:53:53 +0200 Subject: [PATCH 2/3] refactor(toggle): uses new UIEventManager --- src/components/toggle/toggle.ts | 51 +++++++-------------------------- 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/src/components/toggle/toggle.ts b/src/components/toggle/toggle.ts index 6504e00b15..13041180fb 100644 --- a/src/components/toggle/toggle.ts +++ b/src/components/toggle/toggle.ts @@ -5,6 +5,7 @@ import {Form} from '../../util/form'; import {isTrueProperty} from '../../util/util'; import {Item} from '../item/item'; import {pointerCoord} from '../../util/dom'; +import {UIEventManager} from '../../util/ui-event-manager'; const TOGGLE_VALUE_ACCESSOR = new Provider( @@ -87,6 +88,7 @@ export class Toggle implements ControlValueAccessor { private _startX: number; private _msPrv: number = 0; private _fn: Function; + private _events: UIEventManager = new UIEventManager(); /** * @private @@ -113,27 +115,14 @@ export class Toggle implements ControlValueAccessor { } } - /** - * @private - */ - private pointerDown(ev: UIEvent) { - if (this._isPrevented(ev)) { - return; - } - + private pointerDown(ev: UIEvent): boolean { this._startX = pointerCoord(ev).x; this._activated = true; + return true; } - /** - * @private - */ private pointerMove(ev: UIEvent) { if (this._startX) { - if (this._isPrevented(ev)) { - return; - } - let currentX = pointerCoord(ev).x; console.debug('toggle, pointerMove', ev.type, currentX); @@ -152,16 +141,8 @@ export class Toggle implements ControlValueAccessor { } } - /** - * @private - */ private pointerUp(ev: UIEvent) { if (this._startX) { - - if (this._isPrevented(ev)) { - return; - } - let endX = pointerCoord(ev).x; if (this.checked) { @@ -188,9 +169,7 @@ export class Toggle implements ControlValueAccessor { this.onChange(this._checked); } - /** - * @private - */ + private _setChecked(isChecked: boolean) { if (isChecked !== this._checked) { this._checked = isChecked; @@ -256,6 +235,11 @@ export class Toggle implements ControlValueAccessor { */ ngAfterContentInit() { this._init = true; + this._events.pointerEventsRef(this._elementRef, + (ev: any) => this.pointerDown(ev), + (ev: any) => this.pointerMove(ev), + (ev: any) => this.pointerUp(ev) + ); } /** @@ -263,20 +247,7 @@ export class Toggle implements ControlValueAccessor { */ ngOnDestroy() { this._form.deregister(this); - } - - /** - * @private - */ - private _isPrevented(ev: UIEvent) { - if (ev.type.indexOf('touch') > -1) { - this._msPrv = Date.now() + 2000; - - } else if (this._msPrv > Date.now() && ev.type.indexOf('mouse') > -1) { - ev.preventDefault(); - ev.stopPropagation(); - return true; - } + this._events.unlistenAll(); } } From 1a58a41cf90e374aebd73df35ad896537950c334 Mon Sep 17 00:00:00 2001 From: "Manu Mtz.-Almeida" Date: Tue, 21 Jun 2016 17:26:07 +0200 Subject: [PATCH 3/3] fix(refresher): only listen for mousemove/touchmove when needed --- src/components/refresher/refresher.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/components/refresher/refresher.ts b/src/components/refresher/refresher.ts index d580c9195b..06a9c72927 100644 --- a/src/components/refresher/refresher.ts +++ b/src/components/refresher/refresher.ts @@ -151,7 +151,7 @@ export class Refresher { * will automatically go into the `refreshing` state. By default, the pull * maximum will be the result of `pullMin + 60`. */ - @Input() pullMax: number = null; + @Input() pullMax: number = this.pullMin + 60; /** * @input {number} How many milliseconds it takes to close the refresher. Default is `280`. @@ -219,16 +219,23 @@ export class Refresher { if (ev.touches && ev.touches.length > 1) { return false; } + if (this.state !== STATE_INACTIVE) { + return false; + } + + let scrollHostScrollTop = this._content.getContentDimensions().scrollTop; + // if the scrollTop is greater than zero then it's + // not possible to pull the content down yet + if (scrollHostScrollTop > 0) { + return false; + } let coord = pointerCoord(ev); console.debug('Pull-to-refresh, onStart', ev.type, 'y:', coord.y); this.startY = this.currentY = coord.y; this.progress = 0; - - if (!this.pullMax) { - this.pullMax = (this.pullMin + 60); - } + this.state = STATE_PULLING; return true; }