diff --git a/src/components/app/test/gesture-collision/page1.html b/src/components/app/test/gesture-collision/page1.html
index d3a3e87585..855443938c 100644
--- a/src/components/app/test/gesture-collision/page1.html
+++ b/src/components/app/test/gesture-collision/page1.html
@@ -58,6 +58,22 @@
+
+ LEFT button
+
+
+
+
+
+
+
+ RIGHT button
+
+
+
+
+
+
diff --git a/src/components/item/item-sliding-gesture.ts b/src/components/item/item-sliding-gesture.ts
index 221331cd6c..f2d5d86bb1 100644
--- a/src/components/item/item-sliding-gesture.ts
+++ b/src/components/item/item-sliding-gesture.ts
@@ -4,35 +4,29 @@ import { List } from '../list/list';
import { closest, Coordinates, pointerCoord } from '../../util/dom';
import { PointerEvents, UIEventManager } from '../../util/ui-event-manager';
import { GestureDelegate, GestureOptions, GesturePriority } from '../../gestures/gesture-controller';
+import { PanGesture } from '../../gestures/drag-gesture';
const DRAG_THRESHOLD = 10;
const MAX_ATTACK_ANGLE = 20;
-export class ItemSlidingGesture {
+export class ItemSlidingGesture extends PanGesture {
private preSelectedContainer: ItemSliding = null;
private selectedContainer: ItemSliding = null;
private openContainer: ItemSliding = null;
- private events: UIEventManager = new UIEventManager(false);
- private panDetector: PanXRecognizer = new PanXRecognizer(DRAG_THRESHOLD, MAX_ATTACK_ANGLE);
- private pointerEvents: PointerEvents;
private firstCoordX: number;
private firstTimestamp: number;
- private gesture: GestureDelegate;
constructor(public list: List) {
- this.gesture = list.gestureCtrl.create('item-sliding', {
- priority: GesturePriority.Interactive,
- });
-
- this.pointerEvents = this.events.pointerEvents({
- element: list.getNativeElement(),
- pointerDown: this.pointerStart.bind(this),
- pointerMove: this.pointerMove.bind(this),
- pointerUp: this.pointerEnd.bind(this),
+ super(list.getNativeElement(), {
+ maxAngle: MAX_ATTACK_ANGLE,
+ threshold: DRAG_THRESHOLD,
+ gesture: list.gestureCtrl.create('item-sliding', {
+ priority: GesturePriority.SlidingItem,
+ })
});
}
- private pointerStart(ev: any): boolean {
+ canStart(ev: any): boolean {
if (this.selectedContainer) {
return false;
}
@@ -42,85 +36,50 @@ export class ItemSlidingGesture {
this.closeOpened();
return false;
}
-
// Close open container if it is not the selected one.
if (container !== this.openContainer) {
this.closeOpened();
}
- // Try to start gesture
- if (!this.gesture.start()) {
- this.gesture.release();
- return false;
- }
-
let coord = pointerCoord(ev);
this.preSelectedContainer = container;
- this.panDetector.start(coord);
this.firstCoordX = coord.x;
this.firstTimestamp = Date.now();
return true;
}
- private pointerMove(ev: any) {
- if (this.selectedContainer) {
- this.onDragMove(ev);
- return;
- }
+ onDragStart(ev: any) {
+ ev.preventDefault();
+
let coord = pointerCoord(ev);
- if (this.panDetector.detect(coord)) {
- if (this.panDetector.isPanX() && this.gesture.capture()) {
- this.onDragStart(ev, coord);
- return;
- }
-
- // Detection/capturing was not successful, aborting!
- this.closeOpened();
- this.pointerEvents.stop();
- }
- }
-
- private pointerEnd(ev: any) {
- this.gesture.release();
- if (this.selectedContainer) {
- this.onDragEnd(ev);
- } else {
- this.closeOpened();
- }
- }
-
- private onDragStart(ev: any, coord: Coordinates): boolean {
- let container = getContainer(ev);
- if (!container) {
- console.debug('onDragStart, no itemContainerEle');
- return false;
- }
- ev.preventDefault();
-
this.selectedContainer = this.openContainer = this.preSelectedContainer;
- container.startSliding(coord.x);
+ this.selectedContainer.startSliding(coord.x);
}
- private onDragMove(ev: any) {
- let coordX = pointerCoord(ev).x;
+ onDragMove(ev: any) {
ev.preventDefault();
+
+ let coordX = pointerCoord(ev).x;
this.selectedContainer.moveSliding(coordX);
}
- private onDragEnd(ev: any) {
+ onDragEnd(ev: any) {
ev.preventDefault();
+
let coordX = pointerCoord(ev).x;
let deltaX = (coordX - this.firstCoordX);
let deltaT = (Date.now() - this.firstTimestamp);
-
let openAmount = this.selectedContainer.endSliding(deltaX / deltaT);
this.selectedContainer = null;
this.preSelectedContainer = null;
}
+ notCaptured(ev: any) {
+ this.closeOpened();
+ }
+
closeOpened(): boolean {
this.selectedContainer = null;
- this.gesture.release();
if (this.openContainer) {
this.openContainer.close();
@@ -131,8 +90,7 @@ export class ItemSlidingGesture {
}
destroy() {
- this.gesture.destroy();
- this.events.unlistenAll();
+ super.destroy();
this.closeOpened();
this.list = null;
@@ -148,70 +106,4 @@ function getContainer(ev: any): ItemSliding {
return (ele)['$ionComponent'];
}
return null;
-}
-
-class AngleRecognizer {
- private startCoord: Coordinates;
- private sumCoord: Coordinates;
- private dirty: boolean;
- private _angle: any = null;
- private threshold: number;
-
- constructor(threshold: number) {
- this.threshold = threshold ** 2;
- }
-
- start(coord: Coordinates) {
- this.startCoord = coord;
- this._angle = 0;
- this.dirty = true;
- }
-
- angle(): any {
- return this._angle;
- }
-
- detect(coord: Coordinates): boolean {
- if (!this.dirty) {
- return false;
- }
- let deltaX = (coord.x - this.startCoord.x);
- let deltaY = (coord.y - this.startCoord.y);
- let distance = deltaX * deltaX + deltaY * deltaY;
- if (distance >= this.threshold) {
- this._angle = Math.atan2(deltaY, deltaX);
- this.dirty = false;
- return true;
- }
- return false;
- }
-}
-
-
-class PanXRecognizer extends AngleRecognizer {
- private _isPanX: boolean;
- private maxAngle: number;
-
- constructor(threshold: number, maxAngle: number) {
- super(threshold);
- this.maxAngle = maxAngle * (Math.PI / 180);
- }
-
- start(coord: Coordinates) {
- super.start(coord);
- this._isPanX = false;
- }
-
- isPanX(): boolean {
- return this._isPanX;
- }
-
- detect(coord: Coordinates): boolean {
- if (super.detect(coord)) {
- let angle = Math.abs(this.angle());
- this._isPanX = (angle < this.maxAngle || Math.abs(angle - Math.PI) < this.maxAngle);
- return true;
- }
- return false;
- }
-}
+}
\ No newline at end of file
diff --git a/src/components/list/list.ts b/src/components/list/list.ts
index 18a0eb6f86..1e92ec822f 100644
--- a/src/components/list/list.ts
+++ b/src/components/list/list.ts
@@ -92,6 +92,7 @@ export class List extends Ion {
} else if (!this._slidingGesture) {
console.debug('enableSlidingItems');
this._slidingGesture = new ItemSlidingGesture(this);
+ this._slidingGesture.listen();
}
}
diff --git a/src/components/menu/menu-gestures.ts b/src/components/menu/menu-gestures.ts
index 0284bf1529..dc55a4f8ee 100644
--- a/src/components/menu/menu-gestures.ts
+++ b/src/components/menu/menu-gestures.ts
@@ -2,69 +2,35 @@ import { Menu } from './menu';
import { SlideEdgeGesture } from '../../gestures/slide-edge-gesture';
import { SlideData } from '../../gestures/slide-gesture';
import { assign } from '../../util/util';
-import { GestureDelegate, GesturePriority } from '../../gestures/gesture-controller';
-
-const DEGREES_TO_RADIANS = Math.PI / 180;
-const MIN_COSINE = Math.cos(40 * DEGREES_TO_RADIANS);
+import { GesturePriority } from '../../gestures/gesture-controller';
/**
* Gesture attached to the content which the menu is assigned to
*/
export class MenuContentGesture extends SlideEdgeGesture {
- gesture: GestureDelegate;
constructor(public menu: Menu, contentEle: HTMLElement, options: any = {}) {
super(contentEle, assign({
direction: 'x',
edge: menu.side,
threshold: 0,
- maxEdgeStart: menu.maxEdgeStart || 75
+ maxEdgeStart: menu.maxEdgeStart || 50,
+ maxAngle: 40,
+ gesture: menu.gestureCtrl.create('menu-swipe', {
+ priority: GesturePriority.MenuSwipe,
+ })
}, options));
-
- this.gesture = menu.gestureCtrl.create('menu-swipe', {
- priority: GesturePriority.NavigationOptional,
- });
}
canStart(ev: any): boolean {
- if (this.shouldStart(ev)) {
- return this.gesture.capture();
- }
- this.gesture.release();
- return false;
- }
-
- shouldStart(ev: any): boolean {
let menu = this.menu;
- if (!menu.enabled || !menu.swipeEnabled) {
- console.debug('menu can not start, isEnabled:', menu.enabled, 'isSwipeEnabled:', menu.swipeEnabled, 'side:', menu.side);
- return false;
- }
-
- if (ev.distance > 50) {
- // the distance is longer than you'd expect a side menu swipe to be
- console.debug('menu can not start, distance too far:', ev.distance, 'side:', menu.side);
- return false;
- }
-
- console.debug('menu shouldCapture,', menu.side, 'isOpen', menu.isOpen, 'angle', ev.angle, 'distance', ev.distance);
-
- if (menu.isOpen) {
- return true;
- }
-
- let cosine = Math.cos(ev.angle * DEGREES_TO_RADIANS);
- if (menu.side === 'right') {
- if (cosine < -MIN_COSINE) {
- return super.canStart(ev);
- }
- } else {
- if (cosine > MIN_COSINE) {
- return super.canStart(ev);
- }
- }
- return false;
+ return (
+ menu.enabled &&
+ menu.swipeEnabled &&
+ (menu.isOpen || super.canStart(ev))
+ );
}
+
// Set CSS, then wait one frame for it to apply before sliding starts
onSlideBeforeStart(slide: SlideData, ev: any) {
console.debug('menu gesture, onSlideBeforeStart', this.menu.side);
@@ -75,29 +41,26 @@ export class MenuContentGesture extends SlideEdgeGesture {
let z = (this.menu.side === 'right' ? slide.min : slide.max);
let stepValue = (slide.distance / z);
console.debug('menu gesture, onSlide', this.menu.side, 'distance', slide.distance, 'min', slide.min, 'max', slide.max, 'z', z, 'stepValue', stepValue);
- ev.srcEvent.preventDefault();
ev.preventDefault();
this.menu.swipeProgress(stepValue);
}
onSlideEnd(slide: SlideData, ev: any) {
- this.gesture.release();
-
let z = (this.menu.side === 'right' ? slide.min : slide.max);
let currentStepValue = (slide.distance / z);
-
+ let velocity = slide.velocity;
z = Math.abs(z * 0.5);
- let shouldCompleteRight = (ev.velocityX >= 0)
- && (ev.velocityX > 0.2 || slide.delta > z);
+ let shouldCompleteRight = (velocity >= 0)
+ && (velocity > 0.2 || slide.delta > z);
- let shouldCompleteLeft = (ev.velocityX <= 0)
- && (ev.velocityX < -0.2 || slide.delta < -z);
+ let shouldCompleteLeft = (velocity <= 0)
+ && (velocity < -0.2 || slide.delta < -z);
console.debug(
'menu gesture, onSlide', this.menu.side,
'distance', slide.distance,
'delta', slide.delta,
- 'velocityX', ev.velocityX,
+ 'velocity', velocity,
'min', slide.min,
'max', slide.max,
'shouldCompleteLeft', shouldCompleteLeft,
@@ -131,27 +94,5 @@ export class MenuContentGesture extends SlideEdgeGesture {
max: this.menu.width()
};
}
-
- unlisten() {
- this.gesture.release();
- super.unlisten();
- }
-
- destroy() {
- this.gesture.destroy();
- super.destroy();
- }
}
-
-/**
- * Gesture attached to the actual menu itself
- */
-export class MenuTargetGesture extends MenuContentGesture {
- constructor(menu: Menu, menuEle: HTMLElement) {
- super(menu, menuEle, {
- maxEdgeStart: 0
- });
- this.gesture.priority++;
- }
-}
diff --git a/src/components/menu/menu.ts b/src/components/menu/menu.ts
index 11f8e0e7b0..0f12205dfb 100644
--- a/src/components/menu/menu.ts
+++ b/src/components/menu/menu.ts
@@ -5,7 +5,7 @@ import { Config } from '../../config/config';
import { Ion } from '../ion';
import { isTrueProperty } from '../../util/util';
import { Keyboard } from '../../util/keyboard';
-import { MenuContentGesture, MenuTargetGesture } from './menu-gestures';
+import { MenuContentGesture } from './menu-gestures';
import { MenuController } from './menu-controller';
import { MenuType } from './menu-types';
import { Platform } from '../../platform/platform';
@@ -191,8 +191,7 @@ import { GestureController } from '../../gestures/gesture-controller';
export class Menu extends Ion {
private _preventTime: number = 0;
private _cntEle: HTMLElement;
- private _cntGesture: MenuTargetGesture;
- private _menuGesture: MenuContentGesture;
+ private _cntGesture: MenuContentGesture;
private _type: MenuType;
private _resizeUnreg: Function;
private _isEnabled: boolean = true;
@@ -337,8 +336,7 @@ export class Menu extends Ion {
self._renderer.setElementAttribute(self._elementRef.nativeElement, 'type', self.type);
// add the gestures
- self._cntGesture = new MenuContentGesture(self, self.getContentElement());
- self._menuGesture = new MenuTargetGesture(self, self.getNativeElement());
+ self._cntGesture = new MenuContentGesture(self, document.body);
// register listeners if this menu is enabled
// check if more than one menu is on the same side
@@ -389,16 +387,12 @@ export class Menu extends Ion {
if (self._isEnabled && self._isSwipeEnabled && !self._cntGesture.isListening) {
// should listen, but is not currently listening
console.debug('menu, gesture listen', self.side);
- self._zone.runOutsideAngular(function() {
- self._cntGesture.listen();
- self._menuGesture.listen();
- });
+ self._cntGesture.listen();
} else if (self._cntGesture.isListening && (!self._isEnabled || !self._isSwipeEnabled)) {
// should not listen, but is currently listening
console.debug('menu, gesture unlisten', self.side);
self._cntGesture.unlisten();
- self._menuGesture.unlisten();
}
}
}
@@ -625,7 +619,6 @@ export class Menu extends Ion {
ngOnDestroy() {
this._menuCtrl.unregister(this);
this._cntGesture && this._cntGesture.destroy();
- this._menuGesture && this._menuGesture.destroy();
this._type && this._type.destroy();
this._resizeUnreg && this._resizeUnreg();
this._cntEle = null;
diff --git a/src/components/nav/swipe-back.ts b/src/components/nav/swipe-back.ts
index 8a162d7e2a..32a7458292 100644
--- a/src/components/nav/swipe-back.ts
+++ b/src/components/nav/swipe-back.ts
@@ -8,8 +8,6 @@ import { SlideEdgeGesture } from '../../gestures/slide-edge-gesture';
export class SwipeBackGesture extends SlideEdgeGesture {
- private gesture: GestureDelegate;
-
constructor(
element: HTMLElement,
options: any,
@@ -18,32 +16,26 @@ export class SwipeBackGesture extends SlideEdgeGesture {
) {
super(element, assign({
direction: 'x',
- maxEdgeStart: 75
+ maxEdgeStart: 75,
+ gesture: gestureCtlr.create('goback-swipe', {
+ priority: GesturePriority.GoBackSwipe,
+ })
}, options));
-
- this.gesture = gestureCtlr.create('goback-swipe', {
- priority: GesturePriority.Navigation,
- });
}
canStart(ev: any): boolean {
- this.gesture.release();
-
// 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
return (
- ev.angle > -40 &&
- ev.angle < 40 &&
- ev.distance < 50 &&
this._nav.canSwipeBack() &&
- super.canStart(ev) &&
- this.gesture.capture()
+ super.canStart(ev)
);
}
+
onSlideBeforeStart(slideData: SlideData, ev: any) {
- console.debug('swipeBack, onSlideBeforeStart', ev.srcEvent.type);
+ console.debug('swipeBack, onSlideBeforeStart', ev.type);
this._nav.swipeBackStart();
}
@@ -54,25 +46,10 @@ export class SwipeBackGesture extends SlideEdgeGesture {
}
onSlideEnd(slide: SlideData, ev: any) {
- let shouldComplete = (Math.abs(ev.velocityX) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5);
-
+ let shouldComplete = (Math.abs(slide.velocity) > 0.2 || Math.abs(slide.delta) > Math.abs(slide.max) * 0.5);
let currentStepValue = (slide.distance / slide.max);
console.debug('swipeBack, onSlideEnd, shouldComplete', shouldComplete, 'currentStepValue', currentStepValue);
-
this._nav.swipeBackEnd(shouldComplete, currentStepValue);
-
- this.gesture.release();
}
-
- unlisten() {
- this.gesture.release();
- super.unlisten();
- }
-
- destroy() {
- this.gesture.destroy();
- super.destroy();
- }
-
}
diff --git a/src/components/refresher/refresher.ts b/src/components/refresher/refresher.ts
index 03081e14da..7acebde8c2 100644
--- a/src/components/refresher/refresher.ts
+++ b/src/components/refresher/refresher.ts
@@ -201,7 +201,7 @@ export class Refresher {
constructor(@Host() private _content: Content, private _zone: NgZone, gestureCtrl: GestureController) {
_content.addCssClass('has-refresher');
this._gesture = gestureCtrl.create('refresher', {
- priority: GesturePriority.Interactive,
+ priority: GesturePriority.Refresher,
});
}
diff --git a/src/gestures/drag-gesture.ts b/src/gestures/drag-gesture.ts
index 87dd7a2624..f1ea44d07c 100644
--- a/src/gestures/drag-gesture.ts
+++ b/src/gestures/drag-gesture.ts
@@ -1,42 +1,142 @@
-import { Gesture } from './gesture';
+
import { defaults } from '../util';
+import { GestureDelegate } from '../gestures/gesture-controller';
+import { PointerEvents, UIEventManager } from '../util/ui-event-manager';
+import { PanRecognizer } from './recognizers';
+import { pointerCoord, Coordinates } from '../util/dom';
/**
* @private
*/
+export interface PanGestureConfig {
+ threshold?: number;
+ maxAngle?: number;
+ direction?: 'x' | 'y';
+ gesture?: GestureDelegate;
+}
-export class DragGesture extends Gesture {
- public dragging: boolean;
+/**
+ * @private
+ */
+export class PanGesture {
+ private dragging: boolean;
+ private events: UIEventManager = new UIEventManager(false);
+ private pointerEvents: PointerEvents;
+ private detector: PanRecognizer;
+ private started: boolean = false;
+ private captured: boolean = false;
+ public isListening: boolean = false;
+ protected gestute: GestureDelegate;
+ protected direction: string;
- constructor(element: HTMLElement, opts = {}) {
- defaults(opts, {});
- super(element, opts);
+ constructor(private element: HTMLElement, opts: PanGestureConfig = {}) {
+ defaults(opts, {
+ threshold: 20,
+ maxAngle: 40,
+ direction: 'x'
+ });
+ this.gestute = opts.gesture;
+ this.direction = opts.direction;
+ this.detector = new PanRecognizer(opts.direction, opts.threshold, opts.maxAngle);
}
listen() {
- super.listen();
-
- this.on('panstart', (ev: UIEvent) => {
- if (this.onDragStart(ev) !== false) {
- this.dragging = true;
- }
- });
-
- this.on('panmove', (ev: UIEvent) => {
- if (!this.dragging) return;
- if (this.onDrag(ev) === false) {
- this.dragging = false;
- }
- });
-
- this.on('panend', (ev: UIEvent) => {
- if (!this.dragging) return;
- this.onDragEnd(ev);
- this.dragging = false;
- });
+ if (!this.isListening) {
+ this.pointerEvents = this.events.pointerEvents({
+ element: this.element,
+ pointerDown: this.pointerDown.bind(this),
+ pointerMove: this.pointerMove.bind(this),
+ pointerUp: this.pointerUp.bind(this),
+ });
+ this.isListening = true;
+ }
}
- onDrag(ev: any): boolean { return true; }
- onDragStart(ev: any): boolean { return true; }
- onDragEnd(ev: any): void {}
+ unlisten() {
+ this.gestute && this.gestute.release();
+ this.events.unlistenAll();
+ this.isListening = false;
+ }
+
+ destroy() {
+ this.gestute && this.gestute.destroy();
+ this.unlisten();
+ this.element = null;
+ }
+
+ pointerDown(ev: any): boolean {
+ if (this.started) {
+ return;
+ }
+ if (!this.canStart(ev)) {
+ return false;
+ }
+ if (this.gestute) {
+ // Release fallback
+ this.gestute.release();
+ // Start gesture
+ if (!this.gestute.start()) {
+ return false;
+ }
+ }
+
+ let coord = pointerCoord(ev);
+ this.detector.start(coord);
+ this.started = true;
+ this.captured = false;
+ return true;
+ }
+
+ pointerMove(ev: any) {
+ if (!this.started) {
+ return;
+ }
+ if (this.captured) {
+ this.onDragMove(ev);
+ return;
+ }
+ let coord = pointerCoord(ev);
+ if (this.detector.detect(coord)) {
+
+ if (this.detector.pan() !== 0 && this.canCapture(ev) &&
+ (!this.gestute || this.gestute.capture())) {
+ this.onDragStart(ev);
+ this.captured = true;
+ return;
+ }
+
+ // Detection/capturing was not successful, aborting!
+ this.started = false;
+ this.captured = false;
+ this.pointerEvents.stop();
+ this.notCaptured(ev);
+ }
+ }
+
+ pointerUp(ev: any) {
+ if (!this.started) {
+ return;
+ }
+ this.gestute && this.gestute.release();
+
+ if (this.captured) {
+ this.onDragEnd(ev);
+ } else {
+ this.notCaptured(ev);
+ }
+ this.captured = false;
+ this.started = false;
+ }
+
+ getNativeElement(): HTMLElement {
+ return this.element;
+ }
+
+ // Implemented in a subclass
+ canStart(ev: any): boolean { return true; }
+ canCapture(ev: any): boolean { return true; }
+ onDragStart(ev: any) { }
+ onDragMove(ev: any) { }
+ onDragEnd(ev: any) { }
+ notCaptured(ev: any) { }
}
diff --git a/src/gestures/gesture-controller.ts b/src/gestures/gesture-controller.ts
index 257bb8ccbf..f768dc252e 100644
--- a/src/gestures/gesture-controller.ts
+++ b/src/gestures/gesture-controller.ts
@@ -4,11 +4,16 @@ import { App } from '../components/app/app';
export const enum GesturePriority {
Minimun = -10000,
- NavigationOptional = -20,
- Navigation = -10,
+ VeryLow = -20,
+ Low = -10,
Normal = 0,
- Interactive = 10,
- Input = 20,
+ High = 10,
+ VeryHigh = 20,
+
+ SlidingItem = Low,
+ MenuSwipe = High,
+ GoBackSwipe = VeryHigh,
+ Refresher = Normal,
}
export const enum DisableScroll {
diff --git a/src/gestures/recognizers.ts b/src/gestures/recognizers.ts
new file mode 100644
index 0000000000..ad63faaebe
--- /dev/null
+++ b/src/gestures/recognizers.ts
@@ -0,0 +1,58 @@
+import { pointerCoord, Coordinates } from '../util/dom';
+
+export class PanRecognizer {
+ private startCoord: Coordinates;
+ private dirty: boolean = false;
+ private threshold: number;
+ private maxCosine: number;
+ private _angle: any = 0;
+ private _isPan: number = 0;
+
+ constructor(private direction: string, threshold: number, maxAngle: number) {
+ let radians = maxAngle * (Math.PI / 180);
+ this.maxCosine = Math.cos(radians);
+ this.threshold = threshold * threshold;
+ }
+
+ start(coord: Coordinates) {
+ this.startCoord = coord;
+ this._angle = 0;
+ this._isPan = 0;
+ this.dirty = true;
+ }
+
+ detect(coord: Coordinates): boolean {
+ if (!this.dirty) {
+ return false;
+ }
+ let deltaX = (coord.x - this.startCoord.x);
+ let deltaY = (coord.y - this.startCoord.y);
+ let distance = deltaX * deltaX + deltaY * deltaY;
+ if (distance >= this.threshold) {
+ let angle = Math.atan2(deltaY, deltaX);
+ let cosine = (this.direction === 'y')
+ ? Math.sin(angle)
+ : Math.cos(angle);
+
+ this._angle = angle;
+ if (cosine > this.maxCosine) {
+ this._isPan = 1;
+ } else if (cosine < -this.maxCosine) {
+ this._isPan = -1;
+ } else {
+ this._isPan = 0;
+ }
+ this.dirty = false;
+ return true;
+ }
+ return false;
+ }
+
+ angle(): any {
+ return this._angle;
+ }
+
+ pan(): number {
+ return this._isPan;
+ }
+}
diff --git a/src/gestures/slide-edge-gesture.ts b/src/gestures/slide-edge-gesture.ts
index 74b5d18567..d40425f06e 100644
--- a/src/gestures/slide-edge-gesture.ts
+++ b/src/gestures/slide-edge-gesture.ts
@@ -1,6 +1,6 @@
-import {SlideGesture} from './slide-gesture';
-import {defaults} from '../util/util';
-import {windowDimensions} from '../util/dom';
+import { SlideGesture } from './slide-gesture';
+import { defaults } from '../util/util';
+import { pointerCoord, windowDimensions } from '../util/dom';
/**
* @private
@@ -22,8 +22,9 @@ export class SlideEdgeGesture extends SlideGesture {
}
canStart(ev: any): boolean {
+ let coord = pointerCoord(ev);
this._d = this.getContainerDimensions();
- return this.edges.every(edge => this._checkEdge(edge, ev.center));
+ return this.edges.every(edge => this._checkEdge(edge, coord));
}
getContainerDimensions() {
diff --git a/src/gestures/slide-gesture.ts b/src/gestures/slide-gesture.ts
index dca0d0f19f..421ed1e050 100644
--- a/src/gestures/slide-gesture.ts
+++ b/src/gestures/slide-gesture.ts
@@ -1,16 +1,15 @@
-import {DragGesture} from './drag-gesture';
-import {clamp} from '../util';
-
+import { PanGesture } from './drag-gesture';
+import { clamp } from '../util';
+import { pointerCoord } from '../util/dom';
/**
* @private
*/
-export class SlideGesture extends DragGesture {
+export class SlideGesture extends PanGesture {
public slide: SlideData = null;
constructor(element: HTMLElement, opts = {}) {
super(element, opts);
- this.element = element;
}
/*
@@ -20,7 +19,7 @@ export class SlideGesture extends DragGesture {
getSlideBoundaries(slide: SlideData, ev: any) {
return {
min: 0,
- max: this.element.offsetWidth
+ max: this.getNativeElement().offsetWidth
};
}
@@ -33,48 +32,43 @@ export class SlideGesture extends DragGesture {
return 0;
}
- canStart(ev: any): boolean {
- return true;
- }
-
- onDragStart(ev: any): boolean {
- if (!this.canStart(ev)) {
- return false;
- }
-
+ onDragStart(ev: any) {
this.slide = {};
this.onSlideBeforeStart(this.slide, ev);
- var {min, max} = this.getSlideBoundaries(this.slide, ev);
+ let {min, max} = this.getSlideBoundaries(this.slide, ev);
+ let coord = pointerCoord(ev);
this.slide.min = min;
this.slide.max = max;
this.slide.elementStartPos = this.getElementStartPos(this.slide, ev);
- this.slide.pointerStartPos = ev.center[this.direction];
+ this.slide.pos = this.slide.pointerStartPos = coord[this.direction];
+ this.slide.timestamp = Date.now();
this.slide.started = true;
+ this.slide.velocity = 0;
this.onSlideStart(this.slide, ev);
-
- return true;
}
- onDrag(ev: any): boolean {
- if (!this.slide || !this.slide.started) {
- return false;
- }
+ onDragMove(ev: any) {
+ let coord = pointerCoord(ev);
+ let newPos = coord[this.direction];
+ let newTimestamp = Date.now();
+ let velocity = (newPos - this.slide.pos) / (newTimestamp - this.slide.timestamp);
- this.slide.pos = ev.center[this.direction];
+ this.slide.pos = newPos;
+ this.slide.timestamp = newTimestamp;
this.slide.distance = clamp(
this.slide.min,
- this.slide.pos - this.slide.pointerStartPos + this.slide.elementStartPos,
+ newPos - this.slide.pointerStartPos + this.slide.elementStartPos,
this.slide.max
);
- this.slide.delta = this.slide.pos - this.slide.pointerStartPos;
+ this.slide.velocity = velocity;
+ this.slide.delta = newPos - this.slide.pointerStartPos;
this.onSlide(this.slide, ev);
return true;
}
onDragEnd(ev: any) {
- if (!this.slide || !this.slide.started) return;
this.onSlideEnd(this.slide, ev);
this.slide = null;
}
@@ -85,6 +79,9 @@ export class SlideGesture extends DragGesture {
onSlideEnd(slide?: SlideData, ev?: any): void {}
}
+/**
+ * @private
+ */
export interface SlideData {
min?: number;
max?: number;
@@ -92,6 +89,8 @@ export interface SlideData {
delta?: number;
started?: boolean;
pos?: any;
+ timestamp?: number;
pointerStartPos?: number;
elementStartPos?: number;
+ velocity?: number;
}