mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
chore(): begin adding ionic components to mono-repo.
This commit is contained in:
22
packages/ionic-angular/src/gestures/gesture-config.ts
Normal file
22
packages/ionic-angular/src/gestures/gesture-config.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HammerGestureConfig } from '@angular/platform-browser';
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* This class overrides the default Angular gesture config.
|
||||
*/
|
||||
@Injectable()
|
||||
export class IonicGestureConfig extends HammerGestureConfig {
|
||||
|
||||
buildHammer(element: HTMLElement) {
|
||||
const mc = new (<any> window).Hammer(element);
|
||||
|
||||
for (let eventName in this.overrides) {
|
||||
mc.get(eventName).set(this.overrides[eventName]);
|
||||
}
|
||||
|
||||
return mc;
|
||||
}
|
||||
|
||||
}
|
||||
308
packages/ionic-angular/src/gestures/gesture-controller.ts
Normal file
308
packages/ionic-angular/src/gestures/gesture-controller.ts
Normal file
@@ -0,0 +1,308 @@
|
||||
import { forwardRef, Inject, Injectable } from '@angular/core';
|
||||
import { App } from '../components/app/app';
|
||||
import { assert } from '../util/util';
|
||||
|
||||
/** @hidden */
|
||||
export const GESTURE_GO_BACK_SWIPE = 'goback-swipe';
|
||||
|
||||
/** @hidden */
|
||||
export const GESTURE_MENU_SWIPE = 'menu-swipe';
|
||||
|
||||
/** @hidden */
|
||||
export const GESTURE_ITEM_SWIPE = 'item-swipe';
|
||||
|
||||
/** @hidden */
|
||||
export const GESTURE_REFRESHER = 'refresher';
|
||||
|
||||
/** @hidden */
|
||||
export const GESTURE_TOGGLE = 'toggle';
|
||||
|
||||
|
||||
/** @hidden */
|
||||
export const GESTURE_PRIORITY_SLIDING_ITEM = -10;
|
||||
|
||||
/** @hidden */
|
||||
export const GESTURE_PRIORITY_REFRESHER = 0;
|
||||
|
||||
/** @hidden */
|
||||
export const GESTURE_PRIORITY_MENU_SWIPE = 10;
|
||||
|
||||
/** @hidden */
|
||||
export const GESTURE_PRIORITY_GO_BACK_SWIPE = 20;
|
||||
|
||||
/** @hidden */
|
||||
export const GESTURE_PRIORITY_TOGGLE = 30;
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export interface GestureOptions {
|
||||
name: string;
|
||||
disableScroll?: boolean;
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export interface BlockerOptions {
|
||||
disableScroll?: boolean;
|
||||
disable?: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export const BLOCK_ALL: BlockerOptions = {
|
||||
disable: [GESTURE_MENU_SWIPE, GESTURE_GO_BACK_SWIPE],
|
||||
disableScroll: true
|
||||
};
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Injectable()
|
||||
export class GestureController {
|
||||
|
||||
private id: number = 1;
|
||||
private requestedStart: { [eventId: number]: number } = {};
|
||||
private disabledGestures: { [eventName: string]: Set<number> } = {};
|
||||
private disabledScroll: Set<number> = new Set<number>();
|
||||
private capturedID: number = null;
|
||||
|
||||
constructor(@Inject(forwardRef(() => App)) private _app: App) { }
|
||||
|
||||
createGesture(opts: GestureOptions): GestureDelegate {
|
||||
if (!opts.name) {
|
||||
throw new Error('name is undefined');
|
||||
}
|
||||
return new GestureDelegate(opts.name, this.newID(), this,
|
||||
opts.priority || 0,
|
||||
!!opts.disableScroll
|
||||
);
|
||||
}
|
||||
|
||||
createBlocker(opts: BlockerOptions = {}): BlockerDelegate {
|
||||
return new BlockerDelegate(this.newID(), this,
|
||||
opts.disable,
|
||||
!!opts.disableScroll
|
||||
);
|
||||
}
|
||||
|
||||
newID(): number {
|
||||
let id = this.id; this.id++;
|
||||
return id;
|
||||
}
|
||||
|
||||
start(gestureName: string, id: number, priority: number): boolean {
|
||||
if (!this.canStart(gestureName)) {
|
||||
delete this.requestedStart[id];
|
||||
return false;
|
||||
}
|
||||
|
||||
this.requestedStart[id] = priority;
|
||||
return true;
|
||||
}
|
||||
|
||||
capture(gestureName: string, id: number, priority: number): boolean {
|
||||
if (!this.start(gestureName, id, priority)) {
|
||||
return false;
|
||||
}
|
||||
let requestedStart = this.requestedStart;
|
||||
let maxPriority = -10000;
|
||||
for (let gestureID in requestedStart) {
|
||||
maxPriority = Math.max(maxPriority, requestedStart[gestureID]);
|
||||
}
|
||||
|
||||
if (maxPriority === priority) {
|
||||
this.capturedID = id;
|
||||
this.requestedStart = {};
|
||||
console.debug(`${gestureName} captured!`);
|
||||
return true;
|
||||
}
|
||||
delete requestedStart[id];
|
||||
console.debug(`${gestureName} can not start because it is has lower priority`);
|
||||
return false;
|
||||
}
|
||||
|
||||
release(id: number) {
|
||||
delete this.requestedStart[id];
|
||||
if (this.capturedID && id === this.capturedID) {
|
||||
this.capturedID = null;
|
||||
}
|
||||
}
|
||||
|
||||
disableGesture(gestureName: string, id: number) {
|
||||
let set = this.disabledGestures[gestureName];
|
||||
if (!set) {
|
||||
set = new Set<number>();
|
||||
this.disabledGestures[gestureName] = set;
|
||||
}
|
||||
set.add(id);
|
||||
}
|
||||
|
||||
enableGesture(gestureName: string, id: number) {
|
||||
let set = this.disabledGestures[gestureName];
|
||||
if (set) {
|
||||
set.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
disableScroll(id: number) {
|
||||
let isEnabled = !this.isScrollDisabled();
|
||||
this.disabledScroll.add(id);
|
||||
if (this._app && isEnabled && this.isScrollDisabled()) {
|
||||
console.debug('GestureController: Disabling scrolling');
|
||||
this._app._setDisableScroll(true);
|
||||
}
|
||||
}
|
||||
|
||||
enableScroll(id: number) {
|
||||
let isDisabled = this.isScrollDisabled();
|
||||
this.disabledScroll.delete(id);
|
||||
if (this._app && isDisabled && !this.isScrollDisabled()) {
|
||||
console.debug('GestureController: Enabling scrolling');
|
||||
this._app._setDisableScroll(false);
|
||||
}
|
||||
}
|
||||
|
||||
canStart(gestureName: string): boolean {
|
||||
if (this.capturedID) {
|
||||
console.debug(`${gestureName} can not start becuse gesture was already captured`);
|
||||
// a gesture already captured
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.isDisabled(gestureName)) {
|
||||
console.debug(`${gestureName} is disabled`);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
isCaptured(): boolean {
|
||||
return !!this.capturedID;
|
||||
}
|
||||
|
||||
isScrollDisabled(): boolean {
|
||||
return this.disabledScroll.size > 0;
|
||||
}
|
||||
|
||||
isDisabled(gestureName: string): boolean {
|
||||
let disabled = this.disabledGestures[gestureName];
|
||||
return !!(disabled && disabled.size > 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export class GestureDelegate {
|
||||
|
||||
constructor(
|
||||
private name: string,
|
||||
private id: number,
|
||||
private controller: GestureController,
|
||||
private priority: number,
|
||||
private disableScroll: boolean
|
||||
) { }
|
||||
|
||||
canStart(): boolean {
|
||||
if (!this.controller) {
|
||||
assert(false, 'delegate was destroyed');
|
||||
return false;
|
||||
}
|
||||
return this.controller.canStart(this.name);
|
||||
}
|
||||
|
||||
start(): boolean {
|
||||
if (!this.controller) {
|
||||
assert(false, 'delegate was destroyed');
|
||||
return false;
|
||||
}
|
||||
return this.controller.start(this.name, this.id, this.priority);
|
||||
}
|
||||
|
||||
capture(): boolean {
|
||||
if (!this.controller) {
|
||||
assert(false, 'delegate was destroyed');
|
||||
return false;
|
||||
}
|
||||
let captured = this.controller.capture(this.name, this.id, this.priority);
|
||||
if (captured && this.disableScroll) {
|
||||
this.controller.disableScroll(this.id);
|
||||
}
|
||||
return captured;
|
||||
}
|
||||
|
||||
release() {
|
||||
if (!this.controller) {
|
||||
assert(false, 'delegate was destroyed');
|
||||
return;
|
||||
}
|
||||
this.controller.release(this.id);
|
||||
if (this.disableScroll) {
|
||||
this.controller.enableScroll(this.id);
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.release();
|
||||
this.controller = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export class BlockerDelegate {
|
||||
|
||||
blocked: boolean = false;
|
||||
|
||||
constructor(
|
||||
private id: number,
|
||||
private controller: GestureController,
|
||||
private disable: string[],
|
||||
private disableScroll: boolean
|
||||
) { }
|
||||
|
||||
block() {
|
||||
if (!this.controller) {
|
||||
assert(false, 'delegate was destroyed');
|
||||
return;
|
||||
}
|
||||
if (this.disable) {
|
||||
this.disable.forEach(gesture => {
|
||||
this.controller.disableGesture(gesture, this.id);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.disableScroll) {
|
||||
this.controller.disableScroll(this.id);
|
||||
}
|
||||
this.blocked = true;
|
||||
}
|
||||
|
||||
unblock() {
|
||||
if (!this.controller) {
|
||||
assert(false, 'delegate was destroyed');
|
||||
return;
|
||||
}
|
||||
if (this.disable) {
|
||||
this.disable.forEach(gesture => {
|
||||
this.controller.enableGesture(gesture, this.id);
|
||||
});
|
||||
}
|
||||
if (this.disableScroll) {
|
||||
this.controller.enableScroll(this.id);
|
||||
}
|
||||
this.blocked = false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.unblock();
|
||||
this.controller = null;
|
||||
}
|
||||
}
|
||||
79
packages/ionic-angular/src/gestures/gesture.ts
Normal file
79
packages/ionic-angular/src/gestures/gesture.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
import { defaults } from '../util/util';
|
||||
import { Hammer, DIRECTION_HORIZONTAL, DIRECTION_VERTICAL } from './hammer';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
* A gesture recognizer class.
|
||||
*
|
||||
* TODO(mlynch): Re-enable the DOM event simulation that was causing issues (or verify hammer does this already, it might);
|
||||
*/
|
||||
|
||||
export class Gesture {
|
||||
private _hammer: any;
|
||||
private _options: any;
|
||||
private _callbacks: any = {};
|
||||
|
||||
public element: HTMLElement;
|
||||
public direction: string;
|
||||
public isListening: boolean = false;
|
||||
|
||||
constructor(element: HTMLElement, opts: any = {}) {
|
||||
defaults(opts, {
|
||||
domEvents: true
|
||||
});
|
||||
this.element = element;
|
||||
|
||||
// Map 'x' or 'y' string to hammerjs opts
|
||||
this.direction = opts.direction || 'x';
|
||||
opts.direction = this.direction === 'x' ?
|
||||
DIRECTION_HORIZONTAL :
|
||||
DIRECTION_VERTICAL;
|
||||
|
||||
this._options = opts;
|
||||
}
|
||||
|
||||
options(opts: any) {
|
||||
Object.assign(this._options, opts);
|
||||
}
|
||||
|
||||
on(type: string, cb: Function) {
|
||||
if (type === 'pinch' || type === 'rotate') {
|
||||
this._hammer.get(type).set({enable: true});
|
||||
}
|
||||
this._hammer.on(type, cb);
|
||||
(this._callbacks[type] || (this._callbacks[type] = [])).push(cb);
|
||||
}
|
||||
|
||||
off(type: string, cb: Function) {
|
||||
this._hammer.off(type, this._callbacks[type] ? cb : null);
|
||||
}
|
||||
|
||||
listen() {
|
||||
if (!this.isListening) {
|
||||
this._hammer = Hammer(this.element, this._options);
|
||||
}
|
||||
this.isListening = true;
|
||||
}
|
||||
|
||||
unlisten() {
|
||||
let eventType: string;
|
||||
let i: number;
|
||||
|
||||
if (this._hammer && this.isListening) {
|
||||
for (eventType in this._callbacks) {
|
||||
for (i = 0; i < this._callbacks[eventType].length; i++) {
|
||||
this._hammer.off(eventType, this._callbacks[eventType]);
|
||||
}
|
||||
}
|
||||
this._hammer.destroy();
|
||||
}
|
||||
this._callbacks = {};
|
||||
this._hammer = null;
|
||||
this.isListening = false;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.unlisten();
|
||||
this.element = this._options = null;
|
||||
}
|
||||
}
|
||||
2471
packages/ionic-angular/src/gestures/hammer.ts
Normal file
2471
packages/ionic-angular/src/gestures/hammer.ts
Normal file
File diff suppressed because it is too large
Load Diff
191
packages/ionic-angular/src/gestures/pan-gesture.ts
Normal file
191
packages/ionic-angular/src/gestures/pan-gesture.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
import { assert, defaults } from '../util/util';
|
||||
import { DomDebouncer, DomController } from '../platform/dom-controller';
|
||||
import { GestureDelegate } from './gesture-controller';
|
||||
import { PanRecognizer } from './recognizers';
|
||||
import { Platform } from '../platform/platform';
|
||||
import { pointerCoord } from '../util/dom';
|
||||
import { PointerEvents, PointerEventsConfig } from './pointer-events';
|
||||
import { UIEventManager } from './ui-event-manager';
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export class PanGesture {
|
||||
private debouncer: DomDebouncer;
|
||||
private events: UIEventManager;
|
||||
private pointerEvents: PointerEvents;
|
||||
private detector: PanRecognizer;
|
||||
protected started: boolean;
|
||||
private captured: boolean;
|
||||
public isListening: boolean;
|
||||
protected gestute: GestureDelegate;
|
||||
protected direction: string;
|
||||
private eventsConfig: PointerEventsConfig;
|
||||
|
||||
constructor(public plt: Platform, private element: HTMLElement, opts: PanGestureConfig = {}) {
|
||||
defaults(opts, {
|
||||
threshold: 20,
|
||||
maxAngle: 40,
|
||||
direction: 'x',
|
||||
zone: true,
|
||||
capture: false,
|
||||
passive: false,
|
||||
});
|
||||
this.events = new UIEventManager(plt);
|
||||
if (opts.domController) {
|
||||
this.debouncer = opts.domController.debouncer();
|
||||
}
|
||||
this.gestute = opts.gesture;
|
||||
this.direction = opts.direction;
|
||||
this.eventsConfig = {
|
||||
element: this.element,
|
||||
pointerDown: this.pointerDown.bind(this),
|
||||
pointerMove: this.pointerMove.bind(this),
|
||||
pointerUp: this.pointerUp.bind(this),
|
||||
zone: opts.zone,
|
||||
capture: opts.capture,
|
||||
passive: opts.passive
|
||||
};
|
||||
if (opts.threshold > 0) {
|
||||
this.detector = new PanRecognizer(opts.direction, opts.threshold, opts.maxAngle);
|
||||
}
|
||||
}
|
||||
|
||||
listen() {
|
||||
if (!this.isListening) {
|
||||
this.pointerEvents = this.events.pointerEvents(this.eventsConfig);
|
||||
this.isListening = true;
|
||||
}
|
||||
}
|
||||
|
||||
unlisten() {
|
||||
if (this.isListening) {
|
||||
this.gestute && this.gestute.release();
|
||||
this.events.unlistenAll();
|
||||
this.isListening = false;
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.gestute && this.gestute.destroy();
|
||||
this.gestute = null;
|
||||
this.unlisten();
|
||||
this.events.destroy();
|
||||
this.events = this.element = this.gestute = 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;
|
||||
}
|
||||
}
|
||||
this.started = true;
|
||||
this.captured = false;
|
||||
|
||||
const coord = pointerCoord(ev);
|
||||
if (this.detector) {
|
||||
this.detector.start(coord);
|
||||
|
||||
} else {
|
||||
if (!this.tryToCapture(ev)) {
|
||||
this.started = false;
|
||||
this.captured = false;
|
||||
this.gestute.release();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
pointerMove(ev: any) {
|
||||
assert(this.started === true, 'started must be true');
|
||||
if (this.captured) {
|
||||
this.debouncer.write(() => {
|
||||
this.onDragMove(ev);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
assert(this.detector, 'detector has to be valid');
|
||||
const coord = pointerCoord(ev);
|
||||
if (this.detector.detect(coord)) {
|
||||
if (this.detector.pan() !== 0) {
|
||||
if (!this.tryToCapture(ev)) {
|
||||
this.abort(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pointerUp(ev: any) {
|
||||
assert(this.started, 'started failed');
|
||||
this.debouncer.cancel();
|
||||
|
||||
this.gestute && this.gestute.release();
|
||||
|
||||
if (this.captured) {
|
||||
this.onDragEnd(ev);
|
||||
} else {
|
||||
this.notCaptured(ev);
|
||||
}
|
||||
this.captured = false;
|
||||
this.started = false;
|
||||
}
|
||||
|
||||
tryToCapture(ev: any): boolean {
|
||||
assert(this.started === true, 'started has be true');
|
||||
assert(this.captured === false, 'captured has be false');
|
||||
|
||||
if (this.gestute && !this.gestute.capture()) {
|
||||
return false;
|
||||
}
|
||||
this.onDragStart(ev);
|
||||
this.captured = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
abort(ev: any) {
|
||||
this.started = false;
|
||||
this.captured = false;
|
||||
this.gestute.release();
|
||||
this.pointerEvents.stop();
|
||||
this.notCaptured(ev);
|
||||
}
|
||||
|
||||
getNativeElement(): HTMLElement {
|
||||
return this.element;
|
||||
}
|
||||
|
||||
// Implemented in a subclass
|
||||
canStart(ev: any): boolean { return true; }
|
||||
onDragStart(ev: any) { }
|
||||
onDragMove(ev: any) { }
|
||||
onDragEnd(ev: any) { }
|
||||
notCaptured(ev: any) { }
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export interface PanGestureConfig {
|
||||
threshold?: number;
|
||||
maxAngle?: number;
|
||||
direction?: 'x' | 'y';
|
||||
gesture?: GestureDelegate;
|
||||
domController?: DomController;
|
||||
zone?: boolean;
|
||||
capture?: boolean;
|
||||
passive?: boolean;
|
||||
}
|
||||
135
packages/ionic-angular/src/gestures/pointer-events.ts
Normal file
135
packages/ionic-angular/src/gestures/pointer-events.ts
Normal file
@@ -0,0 +1,135 @@
|
||||
import { assert } from '../util/util';
|
||||
import { Platform, EventListenerOptions } from '../platform/platform';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export class PointerEvents {
|
||||
private rmTouchStart: Function = null;
|
||||
private rmTouchMove: Function = null;
|
||||
private rmTouchEnd: Function = null;
|
||||
private rmTouchCancel: Function = null;
|
||||
|
||||
private rmMouseStart: Function = null;
|
||||
private rmMouseMove: Function = null;
|
||||
private rmMouseUp: Function = null;
|
||||
|
||||
private bindTouchEnd: any;
|
||||
private bindMouseUp: any;
|
||||
|
||||
private lastTouchEvent: number = 0;
|
||||
|
||||
mouseWait: number = 2 * 1000;
|
||||
lastEventType: number;
|
||||
|
||||
constructor(
|
||||
private plt: Platform,
|
||||
private ele: any,
|
||||
private pointerDown: any,
|
||||
private pointerMove: any,
|
||||
private pointerUp: any,
|
||||
private option: EventListenerOptions
|
||||
) {
|
||||
assert(ele, 'element can not be null');
|
||||
assert(pointerDown, 'pointerDown can not be null');
|
||||
|
||||
this.bindTouchEnd = this.handleTouchEnd.bind(this);
|
||||
this.bindMouseUp = this.handleMouseUp.bind(this);
|
||||
|
||||
this.rmTouchStart = this.plt.registerListener(ele, 'touchstart', this.handleTouchStart.bind(this), option);
|
||||
this.rmMouseStart = this.plt.registerListener(ele, 'mousedown', this.handleMouseDown.bind(this), option);
|
||||
}
|
||||
|
||||
private handleTouchStart(ev: any) {
|
||||
assert(this.ele, 'element can not be null');
|
||||
assert(this.pointerDown, 'pointerDown can not be null');
|
||||
|
||||
this.lastTouchEvent = Date.now() + this.mouseWait;
|
||||
this.lastEventType = POINTER_EVENT_TYPE_TOUCH;
|
||||
if (!this.pointerDown(ev, POINTER_EVENT_TYPE_TOUCH)) {
|
||||
return;
|
||||
}
|
||||
if (!this.rmTouchMove && this.pointerMove) {
|
||||
this.rmTouchMove = this.plt.registerListener(this.ele, 'touchmove', this.pointerMove, this.option);
|
||||
}
|
||||
if (!this.rmTouchEnd) {
|
||||
this.rmTouchEnd = this.plt.registerListener(this.ele, 'touchend', this.bindTouchEnd, this.option);
|
||||
}
|
||||
if (!this.rmTouchCancel) {
|
||||
this.rmTouchCancel = this.plt.registerListener(this.ele, 'touchcancel', this.bindTouchEnd, this.option);
|
||||
}
|
||||
}
|
||||
|
||||
private handleMouseDown(ev: any) {
|
||||
assert(this.ele, 'element can not be null');
|
||||
assert(this.pointerDown, 'pointerDown can not be null');
|
||||
|
||||
if (this.lastTouchEvent > Date.now()) {
|
||||
console.debug('mousedown event dropped because of previous touch');
|
||||
return;
|
||||
}
|
||||
this.lastEventType = POINTER_EVENT_TYPE_MOUSE;
|
||||
if (!this.pointerDown(ev, POINTER_EVENT_TYPE_MOUSE)) {
|
||||
return;
|
||||
}
|
||||
if (!this.rmMouseMove && this.pointerMove) {
|
||||
this.rmMouseMove = this.plt.registerListener(this.plt.doc(), 'mousemove', this.pointerMove, this.option);
|
||||
}
|
||||
if (!this.rmMouseUp) {
|
||||
this.rmMouseUp = this.plt.registerListener(this.plt.doc(), 'mouseup', this.bindMouseUp, this.option);
|
||||
}
|
||||
}
|
||||
|
||||
private handleTouchEnd(ev: any) {
|
||||
this.stopTouch();
|
||||
this.pointerUp && this.pointerUp(ev, POINTER_EVENT_TYPE_TOUCH);
|
||||
}
|
||||
|
||||
private handleMouseUp(ev: any) {
|
||||
this.stopMouse();
|
||||
this.pointerUp && this.pointerUp(ev, POINTER_EVENT_TYPE_MOUSE);
|
||||
}
|
||||
|
||||
private stopTouch() {
|
||||
this.rmTouchMove && this.rmTouchMove();
|
||||
this.rmTouchEnd && this.rmTouchEnd();
|
||||
this.rmTouchCancel && this.rmTouchCancel();
|
||||
|
||||
this.rmTouchMove = this.rmTouchEnd = this.rmTouchCancel = null;
|
||||
}
|
||||
|
||||
private stopMouse() {
|
||||
this.rmMouseMove && this.rmMouseMove();
|
||||
this.rmMouseUp && this.rmMouseUp();
|
||||
|
||||
this.rmMouseMove = this.rmMouseUp = null;
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.stopTouch();
|
||||
this.stopMouse();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.rmTouchStart && this.rmTouchStart();
|
||||
this.rmMouseStart && this.rmMouseStart();
|
||||
this.stop();
|
||||
this.ele = this.pointerUp = this.pointerMove = this.pointerDown = this.rmTouchStart = this.rmMouseStart = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export const POINTER_EVENT_TYPE_MOUSE = 1;
|
||||
export const POINTER_EVENT_TYPE_TOUCH = 2;
|
||||
|
||||
|
||||
export interface PointerEventsConfig {
|
||||
element?: HTMLElement;
|
||||
pointerDown: (ev: any) => boolean;
|
||||
pointerMove?: (ev: any) => void;
|
||||
pointerUp?: (ev: any) => void;
|
||||
zone?: boolean;
|
||||
capture?: boolean;
|
||||
passive?: boolean;
|
||||
}
|
||||
62
packages/ionic-angular/src/gestures/recognizers.ts
Normal file
62
packages/ionic-angular/src/gestures/recognizers.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { PointerCoordinates } from '../util/dom';
|
||||
|
||||
|
||||
export class PanRecognizer {
|
||||
|
||||
private startCoord: PointerCoordinates;
|
||||
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) {
|
||||
const radians = maxAngle * (Math.PI / 180);
|
||||
this.maxCosine = Math.cos(radians);
|
||||
this.threshold = threshold * threshold;
|
||||
}
|
||||
|
||||
start(coord: PointerCoordinates) {
|
||||
this.startCoord = coord;
|
||||
this._angle = 0;
|
||||
this._isPan = 0;
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
detect(coord: PointerCoordinates): boolean {
|
||||
if (!this.dirty) {
|
||||
return false;
|
||||
}
|
||||
const deltaX = (coord.x - this.startCoord.x);
|
||||
const deltaY = (coord.y - this.startCoord.y);
|
||||
const distance = deltaX * deltaX + deltaY * deltaY;
|
||||
|
||||
if (distance >= this.threshold) {
|
||||
var angle = Math.atan2(deltaY, deltaX);
|
||||
var 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;
|
||||
}
|
||||
}
|
||||
163
packages/ionic-angular/src/gestures/simulator.ts
Normal file
163
packages/ionic-angular/src/gestures/simulator.ts
Normal file
@@ -0,0 +1,163 @@
|
||||
import { PointerCoordinates } from '../util/dom';
|
||||
|
||||
interface Point {
|
||||
coord: PointerCoordinates;
|
||||
duration: number;
|
||||
}
|
||||
|
||||
export class Simulate {
|
||||
private index: number = 0;
|
||||
private points: Point[] = [];
|
||||
public timedelta: number = 1 / 60;
|
||||
|
||||
public static from(x: any, y?: number): Simulate {
|
||||
let s = new Simulate();
|
||||
return s.start(x, y);
|
||||
}
|
||||
|
||||
reset(): Simulate {
|
||||
this.index = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
start(x: any, y?: number): Simulate {
|
||||
this.points = [];
|
||||
return this.to(x, y);
|
||||
}
|
||||
|
||||
to(x: any, y?: number): Simulate {
|
||||
this.newPoint(parseCoordinates(x, y), 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
delta(x: any, y?: number): Simulate {
|
||||
let newPoint = parseCoordinates(x, y);
|
||||
let prevCoord = this.getLastPoint().coord;
|
||||
newPoint.x += prevCoord.x;
|
||||
newPoint.y += prevCoord.y;
|
||||
|
||||
this.newPoint(newPoint, 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
deltaPolar(angle: number, distance: number): Simulate {
|
||||
angle *= Math.PI / 180;
|
||||
let prevCoord = this.getLastPoint().coord;
|
||||
let coord = {
|
||||
x: prevCoord.x + (Math.cos(angle) * distance),
|
||||
y: prevCoord.y + (Math.sin(angle) * distance)
|
||||
};
|
||||
this.newPoint(coord, 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
toPolar(angle: number, distance: number): Simulate {
|
||||
angle *= Math.PI / 180;
|
||||
let coord = {
|
||||
x: Math.cos(angle) * distance,
|
||||
y: Math.sin(angle) * distance
|
||||
};
|
||||
this.newPoint(coord, 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
duration(duration: number): Simulate {
|
||||
this.getLastPoint().duration = duration;
|
||||
return this;
|
||||
}
|
||||
|
||||
velocity(vel: number): Simulate {
|
||||
let p1 = this.getLastPoint();
|
||||
let p2 = this.getPreviousPoint();
|
||||
let d = distance(p1, p2);
|
||||
return this.duration(d / vel);
|
||||
}
|
||||
|
||||
swipeRight(maxAngle: number, distance: number): Simulate {
|
||||
// x------>
|
||||
let angle = randomAngle(maxAngle);
|
||||
return this.deltaPolar(angle, distance);
|
||||
}
|
||||
|
||||
swipeLeft(maxAngle: number, distance: number): Simulate {
|
||||
// <------x
|
||||
let angle = randomAngle(maxAngle) + 180;
|
||||
return this.deltaPolar(angle, distance);
|
||||
}
|
||||
|
||||
swipeTop(maxAngle: number, distance: number): Simulate {
|
||||
let angle = randomAngle(maxAngle) + 90;
|
||||
return this.deltaPolar(angle, distance);
|
||||
}
|
||||
|
||||
swipeBottom(maxAngle: number, distance: number): Simulate {
|
||||
let angle = randomAngle(maxAngle) - 90;
|
||||
return this.deltaPolar(angle, distance);
|
||||
}
|
||||
|
||||
run(callback: Function) {
|
||||
let points = this.points;
|
||||
let len = points.length - 1;
|
||||
let i = 0;
|
||||
for (; i < len; i++) {
|
||||
var p1 = points[i].coord;
|
||||
var p2 = points[i + 1].coord;
|
||||
var duration = points[i + 1].duration;
|
||||
var vectorX = p2.x - p1.x;
|
||||
var vectorY = p2.y - p1.y;
|
||||
var nuSteps = Math.ceil(duration / this.timedelta);
|
||||
vectorX /= nuSteps;
|
||||
vectorY /= nuSteps;
|
||||
for (let j = 0; j < nuSteps; j++) {
|
||||
callback({
|
||||
x: p1.x + vectorX * j,
|
||||
y: p1.y + vectorY * j
|
||||
});
|
||||
}
|
||||
}
|
||||
this.index = i;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private newPoint(coord: PointerCoordinates, duration: number) {
|
||||
this.points.push({
|
||||
coord: coord,
|
||||
duration: duration,
|
||||
});
|
||||
}
|
||||
|
||||
private getLastPoint(): Point {
|
||||
let len = this.points.length;
|
||||
if (len > 0) {
|
||||
return this.points[len - 1];
|
||||
}
|
||||
throw new Error('can not call point');
|
||||
}
|
||||
|
||||
private getPreviousPoint(): Point {
|
||||
let len = this.points.length;
|
||||
if (len > 1) {
|
||||
return this.points[len - 2];
|
||||
}
|
||||
throw new Error('can not call point');
|
||||
}
|
||||
}
|
||||
|
||||
function randomAngle(maxAngle: number): number {
|
||||
return (Math.random() * maxAngle * 2) - maxAngle;
|
||||
}
|
||||
|
||||
function distance(a: PointerCoordinates, b: PointerCoordinates): number {
|
||||
let deltaX = a.x - b.x;
|
||||
let deltaY = a.y - a.y;
|
||||
return Math.hypot(deltaX, deltaY);
|
||||
}
|
||||
|
||||
function parseCoordinates(coord: PointerCoordinates | number, y?: number): PointerCoordinates {
|
||||
if (typeof coord === 'number') {
|
||||
return { x: coord, y: y };
|
||||
}
|
||||
return coord;
|
||||
}
|
||||
65
packages/ionic-angular/src/gestures/slide-edge-gesture.ts
Normal file
65
packages/ionic-angular/src/gestures/slide-edge-gesture.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { SlideGesture } from './slide-gesture';
|
||||
import { defaults } from '../util/util';
|
||||
import { pointerCoord } from '../util/dom';
|
||||
import { Platform } from '../platform/platform';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export class SlideEdgeGesture extends SlideGesture {
|
||||
|
||||
public edges: string[];
|
||||
public maxEdgeStart: any;
|
||||
private _d: any;
|
||||
|
||||
constructor(plt: Platform, element: HTMLElement, opts: any = {}) {
|
||||
defaults(opts, {
|
||||
edge: 'start',
|
||||
maxEdgeStart: 50
|
||||
});
|
||||
super(plt, element, opts);
|
||||
// Can check corners through use of eg 'left top'
|
||||
this.setEdges(opts.edge);
|
||||
this.maxEdgeStart = opts.maxEdgeStart;
|
||||
}
|
||||
|
||||
setEdges(edges: string) {
|
||||
const isRTL = this.plt.isRTL;
|
||||
this.edges = edges.split(' ').map((value) => {
|
||||
switch (value) {
|
||||
case 'start': return isRTL ? 'right' : 'left';
|
||||
case 'end': return isRTL ? 'left' : 'right';
|
||||
default: return value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
canStart(ev: any): boolean {
|
||||
const coord = pointerCoord(ev);
|
||||
this._d = this.getContainerDimensions();
|
||||
return this.edges.every(edge => this._checkEdge(edge, coord));
|
||||
}
|
||||
|
||||
getContainerDimensions() {
|
||||
const plt = this.plt;
|
||||
return {
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: plt.width(),
|
||||
height: plt.height()
|
||||
};
|
||||
}
|
||||
|
||||
_checkEdge(edge: string, pos: any): boolean {
|
||||
const data = this._d;
|
||||
const maxEdgeStart = this.maxEdgeStart;
|
||||
switch (edge) {
|
||||
case 'left': return pos.x <= data.left + maxEdgeStart;
|
||||
case 'right': return pos.x >= data.width - maxEdgeStart;
|
||||
case 'top': return pos.y <= data.top + maxEdgeStart;
|
||||
case 'bottom': return pos.y >= data.height - maxEdgeStart;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
108
packages/ionic-angular/src/gestures/slide-gesture.ts
Normal file
108
packages/ionic-angular/src/gestures/slide-gesture.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { PanGesture } from './pan-gesture';
|
||||
import { clamp, assert } from '../util/util';
|
||||
import { Platform } from '../platform/platform';
|
||||
import { pointerCoord } from '../util/dom';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export class SlideGesture extends PanGesture {
|
||||
public slide: SlideData = null;
|
||||
|
||||
constructor(plt: Platform, element: HTMLElement, opts = {}) {
|
||||
super(plt, element, opts);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the min and max for the slide. pageX/pageY.
|
||||
* Only called on dragstart.
|
||||
*/
|
||||
getSlideBoundaries(slide: SlideData, ev: any) {
|
||||
return {
|
||||
min: 0,
|
||||
max: this.getNativeElement().offsetWidth
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the element's pos when the drag starts.
|
||||
* For example, an open side menu starts at 100% and a closed
|
||||
* sidemenu starts at 0%.
|
||||
*/
|
||||
getElementStartPos(slide: SlideData, ev: any) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
onDragStart(ev: any) {
|
||||
this.onSlideBeforeStart(ev);
|
||||
let coord = <any>pointerCoord(ev);
|
||||
let pos = coord[this.direction];
|
||||
|
||||
this.slide = {
|
||||
min: 0,
|
||||
max: 0,
|
||||
pointerStartPos: pos,
|
||||
pos: pos,
|
||||
timestamp: Date.now(),
|
||||
elementStartPos: 0,
|
||||
started: true,
|
||||
delta: 0,
|
||||
distance: 0,
|
||||
velocity: 0,
|
||||
};
|
||||
|
||||
// TODO: we should run this in the next frame
|
||||
let {min, max} = this.getSlideBoundaries(this.slide, ev);
|
||||
this.slide.min = min;
|
||||
this.slide.max = max;
|
||||
this.slide.elementStartPos = this.getElementStartPos(this.slide, ev);
|
||||
this.onSlideStart(this.slide, ev);
|
||||
}
|
||||
|
||||
onDragMove(ev: any) {
|
||||
let slide: SlideData = this.slide;
|
||||
assert(slide.min !== slide.max, 'slide data must be properly initialized');
|
||||
|
||||
let coord = <any>pointerCoord(ev);
|
||||
let newPos = coord[this.direction];
|
||||
let newTimestamp = Date.now();
|
||||
let velocity = (this.plt.isRTL ? (slide.pos - newPos) : (newPos - slide.pos)) / (newTimestamp - slide.timestamp);
|
||||
|
||||
slide.pos = newPos;
|
||||
slide.timestamp = newTimestamp;
|
||||
slide.distance = clamp(
|
||||
slide.min,
|
||||
(this.plt.isRTL ? slide.pointerStartPos - newPos : newPos - slide.pointerStartPos) + slide.elementStartPos,
|
||||
slide.max
|
||||
);
|
||||
slide.velocity = velocity;
|
||||
slide.delta = (this.plt.isRTL ? slide.pointerStartPos - newPos : newPos - slide.pointerStartPos);
|
||||
this.onSlide(slide, ev);
|
||||
}
|
||||
|
||||
onDragEnd(ev: any) {
|
||||
this.onSlideEnd(this.slide, ev);
|
||||
this.slide = null;
|
||||
}
|
||||
|
||||
onSlideBeforeStart(ev?: any): void {}
|
||||
onSlideStart(slide?: SlideData, ev?: any): void {}
|
||||
onSlide(slide?: SlideData, ev?: any): void {}
|
||||
onSlideEnd(slide?: SlideData, ev?: any): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export interface SlideData {
|
||||
min: number;
|
||||
max: number;
|
||||
distance: number;
|
||||
delta: number;
|
||||
started: boolean;
|
||||
pos: any;
|
||||
timestamp: number;
|
||||
pointerStartPos: number;
|
||||
elementStartPos: number;
|
||||
velocity: number;
|
||||
}
|
||||
@@ -0,0 +1,343 @@
|
||||
import { GestureController, GestureOptions } from '../gesture-controller';
|
||||
|
||||
describe('gesture controller', () => {
|
||||
it('should create an instance of GestureController', () => {
|
||||
let c = new GestureController(null);
|
||||
expect(c.isCaptured()).toEqual(false);
|
||||
expect(c.isScrollDisabled()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should test scrolling enable/disable stack', () => {
|
||||
let c = new GestureController(null);
|
||||
c.enableScroll(1);
|
||||
expect(c.isScrollDisabled()).toEqual(false);
|
||||
|
||||
c.disableScroll(1);
|
||||
expect(c.isScrollDisabled()).toEqual(true);
|
||||
c.disableScroll(1);
|
||||
c.disableScroll(1);
|
||||
expect(c.isScrollDisabled()).toEqual(true);
|
||||
|
||||
c.enableScroll(1);
|
||||
expect(c.isScrollDisabled()).toEqual(false);
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
for (let j = 0; j < 100; j++) {
|
||||
c.disableScroll(j);
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
expect(c.isScrollDisabled()).toEqual(true);
|
||||
c.enableScroll(50 - i);
|
||||
c.enableScroll(i);
|
||||
}
|
||||
expect(c.isScrollDisabled()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should test gesture enable/disable stack', () => {
|
||||
let c = new GestureController(null);
|
||||
c.enableGesture('swipe', 1);
|
||||
expect(c.isDisabled('swipe')).toEqual(false);
|
||||
|
||||
c.disableGesture('swipe', 1);
|
||||
expect(c.isDisabled('swipe')).toEqual(true);
|
||||
c.disableGesture('swipe', 1);
|
||||
c.disableGesture('swipe', 1);
|
||||
expect(c.isDisabled('swipe')).toEqual(true);
|
||||
|
||||
c.enableGesture('swipe', 1);
|
||||
expect(c.isDisabled('swipe')).toEqual(false);
|
||||
|
||||
// Disabling gestures multiple times
|
||||
for (let gestureName = 0; gestureName < 10; gestureName++) {
|
||||
for (let i = 0; i < 50; i++) {
|
||||
for (let j = 0; j < 50; j++) {
|
||||
c.disableGesture(gestureName.toString(), j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let gestureName = 0; gestureName < 10; gestureName++) {
|
||||
for (let i = 0; i < 49; i++) {
|
||||
c.enableGesture(gestureName.toString(), i);
|
||||
}
|
||||
expect(c.isDisabled(gestureName.toString())).toEqual(true);
|
||||
c.enableGesture(gestureName.toString(), 49);
|
||||
expect(c.isDisabled(gestureName.toString())).toEqual(false);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
it('should test if canStart', () => {
|
||||
let c = new GestureController(null);
|
||||
expect(c.canStart('event')).toEqual(true);
|
||||
expect(c.canStart('event1')).toEqual(true);
|
||||
expect(c.canStart('event')).toEqual(true);
|
||||
expect(c['requestedStart']).toEqual({});
|
||||
expect(c.isCaptured()).toEqual(false);
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('should throw error if initializing without a name a gesture delegate without options', () => {
|
||||
let c = new GestureController(null);
|
||||
expect(() => {
|
||||
let a = {};
|
||||
c.createGesture(<GestureOptions>a);
|
||||
}).toThrowError();
|
||||
});
|
||||
|
||||
|
||||
it('should initialize without options', () => {
|
||||
let c = new GestureController(null);
|
||||
|
||||
let g = c.createGesture({
|
||||
name: 'event',
|
||||
});
|
||||
expect(g['name']).toEqual('event');
|
||||
expect(g['priority']).toEqual(0);
|
||||
expect(g['disableScroll']).toEqual(false);
|
||||
expect(g['controller']).toEqual(c);
|
||||
expect(g['id']).toEqual(1);
|
||||
|
||||
let g2 = c.createGesture({ name: 'event2' });
|
||||
expect(g2['id']).toEqual(2);
|
||||
});
|
||||
|
||||
|
||||
it('should initialize a delegate with options', () => {
|
||||
let c = new GestureController(null);
|
||||
let g = c.createGesture({
|
||||
name: 'swipe',
|
||||
priority: -123,
|
||||
disableScroll: true,
|
||||
});
|
||||
expect(g['name']).toEqual('swipe');
|
||||
expect(g['priority']).toEqual(-123);
|
||||
expect(g['disableScroll']).toEqual(true);
|
||||
expect(g['controller']).toEqual(c);
|
||||
expect(g['id']).toEqual(1);
|
||||
});
|
||||
|
||||
it('should test if several gestures can be started', () => {
|
||||
let c = new GestureController(null);
|
||||
let g1 = c.createGesture({ name: 'swipe' });
|
||||
let g2 = c.createGesture({name: 'swipe1', priority: 3});
|
||||
let g3 = c.createGesture({name: 'swipe2', priority: 4});
|
||||
|
||||
for (let i = 0; i < 10; i++) {
|
||||
expect(g1.start()).toEqual(true);
|
||||
expect(g2.start()).toEqual(true);
|
||||
expect(g3.start()).toEqual(true);
|
||||
}
|
||||
expect(c['requestedStart']).toEqual({
|
||||
1: 0,
|
||||
2: 3,
|
||||
3: 4
|
||||
});
|
||||
|
||||
g1.release();
|
||||
g1.release();
|
||||
|
||||
expect(c['requestedStart']).toEqual({
|
||||
2: 3,
|
||||
3: 4
|
||||
});
|
||||
expect(g1.start()).toEqual(true);
|
||||
expect(g2.start()).toEqual(true);
|
||||
g3.destroy();
|
||||
expect(g3['controller']).toBeNull();
|
||||
|
||||
expect(c['requestedStart']).toEqual({
|
||||
1: 0,
|
||||
2: 3,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('should test if several gestures try to capture at the same time', () => {
|
||||
let c = new GestureController(null);
|
||||
let g1 = c.createGesture({name: 'swipe1'});
|
||||
let g2 = c.createGesture({name: 'swipe2', priority: 2 });
|
||||
let g3 = c.createGesture({name: 'swipe3', priority: 3 });
|
||||
let g4 = c.createGesture({name: 'swipe4', priority: 4 });
|
||||
let g5 = c.createGesture({name: 'swipe5', priority: 5 });
|
||||
|
||||
// Low priority capture() returns false
|
||||
expect(g2.start()).toEqual(true);
|
||||
expect(g3.start()).toEqual(true);
|
||||
expect(g1.capture()).toEqual(false);
|
||||
expect(c['requestedStart']).toEqual({
|
||||
2: 2,
|
||||
3: 3
|
||||
});
|
||||
|
||||
// Low priority start() + capture() returns false
|
||||
expect(g2.capture()).toEqual(false);
|
||||
expect(c['requestedStart']).toEqual({
|
||||
3: 3
|
||||
});
|
||||
|
||||
// Higher priority capture() return true
|
||||
expect(g4.capture()).toEqual(true);
|
||||
expect(c.isScrollDisabled()).toEqual(false);
|
||||
expect(c.isCaptured()).toEqual(true);
|
||||
expect(c['requestedStart']).toEqual({});
|
||||
|
||||
// Higher priority can not capture because it is already capture
|
||||
expect(g5.capture()).toEqual(false);
|
||||
expect(g5.canStart()).toEqual(false);
|
||||
expect(g5.start()).toEqual(false);
|
||||
expect(c['requestedStart']).toEqual({});
|
||||
|
||||
// Only captured gesture can release
|
||||
g1.release();
|
||||
g2.release();
|
||||
g3.release();
|
||||
g5.release();
|
||||
expect(c.isCaptured()).toEqual(true);
|
||||
|
||||
// G4 releases
|
||||
g4.release();
|
||||
expect(c.isCaptured()).toEqual(false);
|
||||
|
||||
// Once it was release, any gesture can capture
|
||||
expect(g1.start()).toEqual(true);
|
||||
expect(g1.capture()).toEqual(true);
|
||||
});
|
||||
|
||||
it('should disable scrolling on capture', () => {
|
||||
let c = new GestureController(null);
|
||||
let g = c.createGesture({
|
||||
name: 'goback',
|
||||
disableScroll: true,
|
||||
});
|
||||
let g1 = c.createGesture({ name: 'swipe' });
|
||||
|
||||
g.start();
|
||||
expect(c.isScrollDisabled()).toEqual(false);
|
||||
|
||||
g1.capture();
|
||||
g.capture();
|
||||
expect(c.isScrollDisabled()).toEqual(false);
|
||||
|
||||
g1.release();
|
||||
expect(c.isScrollDisabled()).toEqual(false);
|
||||
|
||||
g.capture();
|
||||
expect(c.isScrollDisabled()).toEqual(true);
|
||||
|
||||
g.destroy();
|
||||
expect(c.isScrollDisabled()).toEqual(false);
|
||||
});
|
||||
|
||||
describe('BlockerDelegate', () => {
|
||||
it('create one', () => {
|
||||
let c = new GestureController(null);
|
||||
let b = c.createBlocker({
|
||||
disableScroll: true,
|
||||
disable: ['event1', 'event2', 'event3', 'event4']
|
||||
});
|
||||
|
||||
expect(b['disable']).toEqual(['event1', 'event2', 'event3', 'event4']);
|
||||
expect(b['disableScroll']).toEqual(true);
|
||||
expect(b['controller']).toEqual(c);
|
||||
expect(b['id']).toEqual(1);
|
||||
|
||||
let b2 = c.createBlocker({
|
||||
disable: ['event2', 'event3', 'event4', 'event5']
|
||||
});
|
||||
|
||||
expect(b2['disable']).toEqual(['event2', 'event3', 'event4', 'event5']);
|
||||
expect(b2['disableScroll']).toEqual(false);
|
||||
expect(b2['controller']).toEqual(c);
|
||||
expect(b2['id']).toEqual(2);
|
||||
|
||||
|
||||
expect(c.isDisabled('event1')).toBeFalsy();
|
||||
expect(c.isDisabled('event2')).toBeFalsy();
|
||||
expect(c.isDisabled('event3')).toBeFalsy();
|
||||
expect(c.isDisabled('event4')).toBeFalsy();
|
||||
expect(c.isDisabled('event5')).toBeFalsy();
|
||||
|
||||
b.block();
|
||||
b.block();
|
||||
|
||||
expect(c.isDisabled('event1')).toBeTruthy();
|
||||
expect(c.isDisabled('event2')).toBeTruthy();
|
||||
expect(c.isDisabled('event3')).toBeTruthy();
|
||||
expect(c.isDisabled('event4')).toBeTruthy();
|
||||
expect(c.isDisabled('event5')).toBeFalsy();
|
||||
|
||||
b2.block();
|
||||
b2.block();
|
||||
b2.block();
|
||||
|
||||
expect(c.isDisabled('event1')).toBeTruthy();
|
||||
expect(c.isDisabled('event2')).toBeTruthy();
|
||||
expect(c.isDisabled('event3')).toBeTruthy();
|
||||
expect(c.isDisabled('event4')).toBeTruthy();
|
||||
expect(c.isDisabled('event5')).toBeTruthy();
|
||||
|
||||
b.unblock();
|
||||
|
||||
expect(c.isDisabled('event1')).toBeFalsy();
|
||||
expect(c.isDisabled('event2')).toBeTruthy();
|
||||
expect(c.isDisabled('event3')).toBeTruthy();
|
||||
expect(c.isDisabled('event4')).toBeTruthy();
|
||||
expect(c.isDisabled('event5')).toBeTruthy();
|
||||
|
||||
b2.destroy();
|
||||
expect(b2['controller']).toBeNull();
|
||||
|
||||
expect(c.isDisabled('event1')).toBeFalsy();
|
||||
expect(c.isDisabled('event2')).toBeFalsy();
|
||||
expect(c.isDisabled('event3')).toBeFalsy();
|
||||
expect(c.isDisabled('event4')).toBeFalsy();
|
||||
expect(c.isDisabled('event5')).toBeFalsy();
|
||||
});
|
||||
|
||||
|
||||
it('should disable some events', () => {
|
||||
let c = new GestureController(null);
|
||||
|
||||
let goback = c.createGesture({ name: 'goback' });
|
||||
expect(goback.canStart()).toEqual(true);
|
||||
|
||||
let g2 = c.createGesture({ name: 'goback2' });
|
||||
expect(g2.canStart()).toEqual(true);
|
||||
|
||||
let g3 = c.createBlocker({
|
||||
disable: ['range', 'goback', 'something']
|
||||
});
|
||||
|
||||
let g4 = c.createBlocker({
|
||||
disable: ['range']
|
||||
});
|
||||
|
||||
g3.block();
|
||||
g4.block();
|
||||
|
||||
// goback is disabled
|
||||
expect(c.isDisabled('range')).toEqual(true);
|
||||
expect(c.isDisabled('goback')).toEqual(true);
|
||||
expect(c.isDisabled('something')).toEqual(true);
|
||||
expect(c.isDisabled('goback2')).toEqual(false);
|
||||
expect(goback.canStart()).toEqual(false);
|
||||
expect(goback.start()).toEqual(false);
|
||||
expect(goback.capture()).toEqual(false);
|
||||
|
||||
// Once g3 is destroyed, goback and something should be enabled
|
||||
g3.destroy();
|
||||
expect(c.isDisabled('range')).toEqual(true);
|
||||
expect(c.isDisabled('goback')).toEqual(false);
|
||||
expect(c.isDisabled('something')).toEqual(false);
|
||||
|
||||
// Once g4 is destroyed, range is also enabled
|
||||
g4.unblock();
|
||||
expect(c.isDisabled('range')).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
205
packages/ionic-angular/src/gestures/test/recognizers.spec.ts
Normal file
205
packages/ionic-angular/src/gestures/test/recognizers.spec.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
|
||||
import { PanRecognizer } from '../recognizers';
|
||||
import { Simulate } from '../simulator';
|
||||
|
||||
describe('recognizers', () => {
|
||||
it('should not fire if it did not start', () => {
|
||||
let p = new PanRecognizer('x', 2, 2);
|
||||
expect(p.pan()).toEqual(0);
|
||||
|
||||
Simulate.from(0, 0).to(99, 0).run((coord: Coordinates) => {
|
||||
expect(p.detect(coord)).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should reset', () => {
|
||||
let p = new PanRecognizer('x', 2, 2);
|
||||
|
||||
p.start({ x: 0, y: 0 });
|
||||
expect(p.pan()).toEqual(0);
|
||||
|
||||
Simulate.from(0, 0).to(10, 0).run((coord: Coordinates) => {
|
||||
p.detect(coord);
|
||||
});
|
||||
expect(p.pan()).toEqual(1);
|
||||
|
||||
p.start({ x: 0, y: 0 });
|
||||
expect(p.pan()).toEqual(0);
|
||||
|
||||
Simulate.from(0, 0).to(-10, 0).run((coord: Coordinates) => {
|
||||
p.detect(coord);
|
||||
});
|
||||
expect(p.pan()).toEqual(-1);
|
||||
});
|
||||
|
||||
it('should fire with large threshold', () => {
|
||||
let detected = false;
|
||||
let p = new PanRecognizer('x', 100, 40);
|
||||
p.start({ x: 0, y: 0 });
|
||||
|
||||
Simulate
|
||||
.from(0, 0).to(99, 0)
|
||||
// Since threshold is 100, it should not fire yet
|
||||
.run((coord: Coordinates) => expect(p.detect(coord)).toEqual(false))
|
||||
|
||||
// Now it should fire
|
||||
.delta(2, 0)
|
||||
.run((coord: Coordinates) => {
|
||||
if (p.detect(coord)) {
|
||||
// it should detect a horizontal pan
|
||||
expect(p.pan()).toEqual(1);
|
||||
detected = true;
|
||||
}
|
||||
})
|
||||
|
||||
// It should not detect again
|
||||
.delta(20, 0)
|
||||
.to(0, 0)
|
||||
.to(102, 0)
|
||||
.run((coord: Coordinates) => expect(p.detect(coord)).toEqual(false));
|
||||
|
||||
expect(detected).toEqual(true);
|
||||
});
|
||||
|
||||
it('should detect swipe left', () => {
|
||||
let p = new PanRecognizer('x', 20, 20);
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(19, 21).delta(-30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(1);
|
||||
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(-19, 21).delta(-30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(1);
|
||||
});
|
||||
|
||||
it('should detect swipe right', () => {
|
||||
let p = new PanRecognizer('x', 20, 20);
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(180 - 19, 21).delta(30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(-1);
|
||||
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(180 + 19, 21).delta(30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(-1);
|
||||
});
|
||||
|
||||
it('should NOT detect swipe left', () => {
|
||||
let p = new PanRecognizer('x', 20, 20);
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(21, 21).delta(-30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(0);
|
||||
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(-21, 21).delta(-30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should NOT detect swipe right', () => {
|
||||
let p = new PanRecognizer('x', 20, 20);
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(180 - 21, 21).delta(30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(0);
|
||||
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(180 + 21, 21).delta(30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(0);
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('should detect swipe top', () => {
|
||||
let p = new PanRecognizer('y', 20, 20);
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(90 - 19, 21).delta(-30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(1);
|
||||
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(90 + 19, 21).delta(-30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(1);
|
||||
});
|
||||
|
||||
it('should detect swipe bottom', () => {
|
||||
let p = new PanRecognizer('y', 20, 20);
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(-90 + 19, 21).delta(30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(-1);
|
||||
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(-90 - 19, 21).delta(30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(-1);
|
||||
});
|
||||
|
||||
it('should NOT detect swipe top', () => {
|
||||
let p = new PanRecognizer('y', 20, 20);
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(90 - 21, 21).delta(-30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(0);
|
||||
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(90 + 21, 21).delta(-30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should NOT detect swipe bottom', () => {
|
||||
let p = new PanRecognizer('y', 20, 20);
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(-90 + 21, 21).delta(30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(0);
|
||||
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(-90 - 21, 21).delta(30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
expect(p.pan()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should NOT confuse between pan Y and X', () => {
|
||||
let p = new PanRecognizer('x', 20, 20);
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).deltaPolar(90, 21).delta(30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
|
||||
expect(p.pan()).toEqual(0);
|
||||
});
|
||||
|
||||
it('should NOT confuse between pan X and Y', () => {
|
||||
let p = new PanRecognizer('y', 20, 20);
|
||||
p.start({ x: 0, y: 0 });
|
||||
Simulate
|
||||
.from(0, 0).delta(30, 0)
|
||||
.run((coord: Coordinates) => p.detect(coord));
|
||||
|
||||
expect(p.pan()).toEqual(0);
|
||||
});
|
||||
});
|
||||
57
packages/ionic-angular/src/gestures/ui-event-manager.ts
Normal file
57
packages/ionic-angular/src/gestures/ui-event-manager.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { PointerEvents, PointerEventsConfig } from './pointer-events';
|
||||
import { Platform, EventListenerOptions } from '../platform/platform';
|
||||
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
export class UIEventManager {
|
||||
private evts: Function[] = [];
|
||||
|
||||
constructor(public plt: Platform) {}
|
||||
|
||||
pointerEvents(config: PointerEventsConfig): PointerEvents {
|
||||
if (!config.element || !config.pointerDown) {
|
||||
console.error('PointerEvents config is invalid');
|
||||
return;
|
||||
}
|
||||
|
||||
const eventListnerOpts: EventListenerOptions = {
|
||||
capture: config.capture,
|
||||
passive: config.passive,
|
||||
zone: config.zone
|
||||
};
|
||||
|
||||
const pointerEvents = new PointerEvents(
|
||||
this.plt,
|
||||
config.element,
|
||||
config.pointerDown,
|
||||
config.pointerMove,
|
||||
config.pointerUp,
|
||||
eventListnerOpts);
|
||||
|
||||
const removeFunc = () => pointerEvents.destroy();
|
||||
this.evts.push(removeFunc);
|
||||
return pointerEvents;
|
||||
}
|
||||
|
||||
listen(ele: any, eventName: string, callback: any, opts: EventListenerOptions): Function {
|
||||
if (ele) {
|
||||
var removeFunc = this.plt.registerListener(ele, eventName, callback, opts);
|
||||
this.evts.push(removeFunc);
|
||||
return removeFunc;
|
||||
}
|
||||
}
|
||||
|
||||
unlistenAll() {
|
||||
this.evts.forEach(unRegEvent => {
|
||||
unRegEvent();
|
||||
});
|
||||
this.evts.length = 0;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.unlistenAll();
|
||||
this.evts = null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user