mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-11-10 00:27:41 +08:00
feat(): added gesture.
This commit is contained in:
173
src/components/gesture/gesture-controller.ts
Normal file
173
src/components/gesture/gesture-controller.ts
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export class GestureController {
|
||||||
|
private id: number = 0;
|
||||||
|
private requestedStart: { [eventId: number]: number } = {};
|
||||||
|
private disabledGestures: { [eventName: string]: Set<number> } = {};
|
||||||
|
private disabledScroll: Set<number> = new Set<number>();
|
||||||
|
private capturedID: number = null;
|
||||||
|
|
||||||
|
|
||||||
|
createGesture(gestureName: string, gesturePriority: number, disableScroll: boolean): GestureDelegate {
|
||||||
|
return new GestureDelegate(this, ++this.id, gestureName, gesturePriority, disableScroll);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = {};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
delete requestedStart[id];
|
||||||
|
|
||||||
|
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) {
|
||||||
|
// a gesture already captured
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.isDisabled(gestureName)) {
|
||||||
|
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];
|
||||||
|
if (disabled && disabled.size > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class GestureDelegate {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private ctrl: GestureController,
|
||||||
|
private id: number,
|
||||||
|
private name: string,
|
||||||
|
private priority: number,
|
||||||
|
private disableScroll: boolean
|
||||||
|
) { }
|
||||||
|
|
||||||
|
canStart(): boolean {
|
||||||
|
if (!this.ctrl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ctrl.canStart(this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
start(): boolean {
|
||||||
|
if (!this.ctrl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.ctrl.start(this.name, this.id, this.priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
capture(): boolean {
|
||||||
|
if (!this.ctrl) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let captured = this.ctrl.capture(this.name, this.id, this.priority);
|
||||||
|
if (captured && this.disableScroll) {
|
||||||
|
this.ctrl.disableScroll(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return captured;
|
||||||
|
}
|
||||||
|
|
||||||
|
release() {
|
||||||
|
if (this.ctrl) {
|
||||||
|
this.ctrl.release(this.id);
|
||||||
|
|
||||||
|
if (this.disableScroll) {
|
||||||
|
this.ctrl.enableScroll(this.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
this.release();
|
||||||
|
this.ctrl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
372
src/components/gesture/gesture.ts
Normal file
372
src/components/gesture/gesture.ts
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
import { applyStyles, getElementReference, pointerCoordX, pointerCoordY } from '../../util/helpers';
|
||||||
|
import { Component, Ionic, Listen, Prop } from '../index';
|
||||||
|
import { GestureCallback, GestureDetail } from '../../util/interfaces';
|
||||||
|
import { GestureController, GestureDelegate } from './gesture-controller';
|
||||||
|
import { PanRecognizer } from './recognizers';
|
||||||
|
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
tag: 'ion-gesture',
|
||||||
|
shadow: false
|
||||||
|
})
|
||||||
|
export class Gesture {
|
||||||
|
private $el: HTMLElement;
|
||||||
|
private detail: GestureDetail = {};
|
||||||
|
private positions: number[] = [];
|
||||||
|
private gesture: GestureDelegate;
|
||||||
|
private lastTouch = 0;
|
||||||
|
private pan: PanRecognizer;
|
||||||
|
private hasCapturedPan = false;
|
||||||
|
private hasPress = false;
|
||||||
|
private hasStartedPan = false;
|
||||||
|
private requiresMove = false;
|
||||||
|
private isMoveQueued = false;
|
||||||
|
|
||||||
|
@Prop() direction: string = 'x';
|
||||||
|
@Prop() gestureName: string = '';
|
||||||
|
@Prop() gesturePriority: number = 0;
|
||||||
|
@Prop() attachTo: string = 'child';
|
||||||
|
@Prop() maxAngle: number = 40;
|
||||||
|
@Prop() threshold: number = 20;
|
||||||
|
@Prop() type: string = 'pan';
|
||||||
|
|
||||||
|
@Prop() canStart: GestureCallback;
|
||||||
|
@Prop() onStart: GestureCallback;
|
||||||
|
@Prop() onMove: GestureCallback;
|
||||||
|
@Prop() onEnd: GestureCallback;
|
||||||
|
@Prop() onPress: GestureCallback;
|
||||||
|
@Prop() notCaptured: GestureCallback;
|
||||||
|
|
||||||
|
|
||||||
|
ionViewDidLoad() {
|
||||||
|
Ionic.controllers.gesture = (Ionic.controllers.gesture || new GestureController());
|
||||||
|
|
||||||
|
this.gesture = (<GestureController>Ionic.controllers.gesture).createGesture(this.gestureName, this.gesturePriority, false);
|
||||||
|
|
||||||
|
const types = this.type.replace(/\s/g, '').toLowerCase().split(',');
|
||||||
|
|
||||||
|
if (types.indexOf('pan') > -1) {
|
||||||
|
this.pan = new PanRecognizer(this.direction, this.threshold, this.maxAngle);
|
||||||
|
this.requiresMove = true;
|
||||||
|
}
|
||||||
|
this.hasPress = (types.indexOf('press') > -1);
|
||||||
|
|
||||||
|
if (this.pan || this.hasPress) {
|
||||||
|
Ionic.listener.enable(this, 'touchstart', true, this.attachTo);
|
||||||
|
Ionic.listener.enable(this, 'mousedown', true, this.attachTo);
|
||||||
|
|
||||||
|
Ionic.dom.write(() => {
|
||||||
|
applyStyles(getElementReference(this.$el, this.attachTo), GESTURE_INLINE_STYLES);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// DOWN *************************
|
||||||
|
|
||||||
|
@Listen('touchstart', { passive: true, enabled: false })
|
||||||
|
onTouchStart(ev: TouchEvent) {
|
||||||
|
this.lastTouch = now(ev);
|
||||||
|
|
||||||
|
this.enableMouse(false);
|
||||||
|
this.enableTouch(true);
|
||||||
|
|
||||||
|
this.pointerDown(ev, this.lastTouch);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Listen('mousedown', { passive: true, enabled: false })
|
||||||
|
onMouseDown(ev: MouseEvent) {
|
||||||
|
const timeStamp = now(ev);
|
||||||
|
|
||||||
|
if (this.lastTouch === 0 || (this.lastTouch + MOUSE_WAIT < timeStamp)) {
|
||||||
|
this.enableMouse(true);
|
||||||
|
this.enableTouch(false);
|
||||||
|
|
||||||
|
this.pointerDown(ev, timeStamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private pointerDown(ev: UIEvent, timeStamp: number): boolean {
|
||||||
|
if (!this.gesture || this.hasStartedPan) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const detail = this.detail;
|
||||||
|
|
||||||
|
detail.startX = detail.currentX = pointerCoordX(ev);
|
||||||
|
detail.startY = detail.currentY = pointerCoordY(ev);
|
||||||
|
detail.startTimeStamp = detail.timeStamp = timeStamp;
|
||||||
|
detail.velocityX = detail.velocityY = detail.deltaX = detail.deltaY = 0;
|
||||||
|
detail.directionX = detail.directionY = detail.velocityDirectionX = detail.velocityDirectionY = null;
|
||||||
|
detail.event = ev;
|
||||||
|
this.positions.length = 0;
|
||||||
|
|
||||||
|
if (this.canStart && this.canStart(detail) === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.positions.push(detail.currentX, detail.currentY, timeStamp);
|
||||||
|
|
||||||
|
// Release fallback
|
||||||
|
this.gesture.release();
|
||||||
|
|
||||||
|
// Start gesture
|
||||||
|
if (!this.gesture.start()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.pan) {
|
||||||
|
this.hasStartedPan = true;
|
||||||
|
this.hasCapturedPan = false;
|
||||||
|
|
||||||
|
this.pan.start(detail.startX, detail.startY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MOVE *************************
|
||||||
|
|
||||||
|
@Listen('touchmove', { passive: true, enabled: false })
|
||||||
|
onTouchMove(ev: TouchEvent) {
|
||||||
|
this.lastTouch = this.detail.timeStamp = now(ev);
|
||||||
|
|
||||||
|
this.pointerMove(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Listen('document:mousemove', { passive: true, enabled: false })
|
||||||
|
onMoveMove(ev: TouchEvent) {
|
||||||
|
const timeStamp = now(ev);
|
||||||
|
|
||||||
|
if (this.lastTouch === 0 || (this.lastTouch + MOUSE_WAIT < timeStamp)) {
|
||||||
|
this.detail.timeStamp = timeStamp;
|
||||||
|
this.pointerMove(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private pointerMove(ev: UIEvent) {
|
||||||
|
const detail = this.detail;
|
||||||
|
this.calcGestureData(ev);
|
||||||
|
|
||||||
|
if (this.pan) {
|
||||||
|
if (this.hasCapturedPan) {
|
||||||
|
|
||||||
|
if (!this.isMoveQueued) {
|
||||||
|
this.isMoveQueued = true;
|
||||||
|
|
||||||
|
Ionic.dom.write(() => {
|
||||||
|
this.isMoveQueued = false;
|
||||||
|
detail.type = 'pan';
|
||||||
|
|
||||||
|
if (this.onMove) {
|
||||||
|
this.onMove(detail);
|
||||||
|
} else {
|
||||||
|
Ionic.emit(this, 'ionGestureMove', { detail: this.detail });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (this.pan.detect(detail.currentX, detail.currentY)) {
|
||||||
|
if (this.pan.isGesture() !== 0) {
|
||||||
|
if (!this.tryToCapturePan(ev)) {
|
||||||
|
this.abortGesture();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private calcGestureData(ev: UIEvent) {
|
||||||
|
const detail = this.detail;
|
||||||
|
detail.currentX = pointerCoordX(ev);
|
||||||
|
detail.currentY = pointerCoordY(ev);
|
||||||
|
detail.deltaX = (detail.currentX - detail.startX);
|
||||||
|
detail.deltaY = (detail.currentY - detail.startY);
|
||||||
|
detail.event = ev;
|
||||||
|
|
||||||
|
// figure out which direction we're movin'
|
||||||
|
detail.directionX = detail.velocityDirectionX = (detail.deltaX > 0 ? 'left' : (detail.deltaX < 0 ? 'right' : null));
|
||||||
|
detail.directionY = detail.velocityDirectionY = (detail.deltaY > 0 ? 'up' : (detail.deltaY < 0 ? 'down' : null));
|
||||||
|
|
||||||
|
const positions = this.positions;
|
||||||
|
positions.push(detail.currentX, detail.currentY, detail.timeStamp);
|
||||||
|
|
||||||
|
var endPos = (positions.length - 1);
|
||||||
|
var startPos = endPos;
|
||||||
|
var timeRange = (detail.timeStamp - 100);
|
||||||
|
|
||||||
|
// move pointer to position measured 100ms ago
|
||||||
|
for (var i = endPos; i > 0 && positions[i] > timeRange; i -= 3) {
|
||||||
|
startPos = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (startPos !== endPos) {
|
||||||
|
// compute relative movement between these two points
|
||||||
|
var movedX = (positions[startPos - 2] - positions[endPos - 2]);
|
||||||
|
var movedY = (positions[startPos - 1] - positions[endPos - 1]);
|
||||||
|
var factor = 16.67 / (positions[endPos] - positions[startPos]);
|
||||||
|
|
||||||
|
// based on XXms compute the movement to apply for each render step
|
||||||
|
detail.velocityX = movedX * factor;
|
||||||
|
detail.velocityY = movedY * factor;
|
||||||
|
|
||||||
|
detail.velocityDirectionX = (movedX > 0 ? 'left' : (movedX < 0 ? 'right' : null));
|
||||||
|
detail.velocityDirectionY = (movedY > 0 ? 'up' : (movedY < 0 ? 'down' : null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private tryToCapturePan(ev: UIEvent): boolean {
|
||||||
|
if (this.gesture && !this.gesture.capture()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.detail.event = ev;
|
||||||
|
|
||||||
|
if (this.onStart) {
|
||||||
|
this.onStart(this.detail);
|
||||||
|
} else {
|
||||||
|
Ionic.emit(this, 'ionGestureStart', { detail: this.detail });
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hasCapturedPan = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private abortGesture() {
|
||||||
|
this.hasStartedPan = false;
|
||||||
|
this.hasCapturedPan = false;
|
||||||
|
|
||||||
|
this.gesture.release();
|
||||||
|
|
||||||
|
this.enable(false);
|
||||||
|
this.notCaptured(this.detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// END *************************
|
||||||
|
|
||||||
|
@Listen('touchend', { passive: true, enabled: false })
|
||||||
|
onTouchEnd(ev: TouchEvent) {
|
||||||
|
this.lastTouch = this.detail.timeStamp = now(ev);
|
||||||
|
|
||||||
|
this.pointerUp(ev);
|
||||||
|
this.enableTouch(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Listen('document:mouseup', { passive: true, enabled: false })
|
||||||
|
onMouseUp(ev: TouchEvent) {
|
||||||
|
const timeStamp = now(ev);
|
||||||
|
|
||||||
|
if (this.lastTouch === 0 || (this.lastTouch + MOUSE_WAIT < timeStamp)) {
|
||||||
|
this.detail.timeStamp = timeStamp;
|
||||||
|
this.pointerUp(ev);
|
||||||
|
this.enableMouse(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private pointerUp(ev: UIEvent) {
|
||||||
|
const detail = this.detail;
|
||||||
|
|
||||||
|
this.gesture && this.gesture.release();
|
||||||
|
|
||||||
|
detail.event = ev;
|
||||||
|
|
||||||
|
this.calcGestureData(ev);
|
||||||
|
|
||||||
|
if (this.pan) {
|
||||||
|
if (this.hasCapturedPan) {
|
||||||
|
detail.type = 'pan';
|
||||||
|
if (this.onEnd) {
|
||||||
|
this.onEnd(detail);
|
||||||
|
} else {
|
||||||
|
Ionic.emit(this, 'ionGestureEnd', { detail: detail });
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (this.hasPress) {
|
||||||
|
this.detectPress();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (this.notCaptured) {
|
||||||
|
this.notCaptured(detail);
|
||||||
|
} else {
|
||||||
|
Ionic.emit(this, 'ionGestureNotCaptured', { detail: detail });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (this.hasPress) {
|
||||||
|
this.detectPress();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.hasCapturedPan = false;
|
||||||
|
this.hasStartedPan = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private detectPress() {
|
||||||
|
const detail = this.detail;
|
||||||
|
|
||||||
|
if (Math.abs(detail.startX - detail.currentX) < 10 && Math.abs(detail.startY - detail.currentY) < 10) {
|
||||||
|
detail.type = 'press';
|
||||||
|
|
||||||
|
if (this.onPress) {
|
||||||
|
this.onPress(detail);
|
||||||
|
} else {
|
||||||
|
Ionic.emit(this, 'ionPress', { detail: detail });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ENABLE LISTENERS *************************
|
||||||
|
|
||||||
|
private enableMouse(shouldEnable: boolean) {
|
||||||
|
if (this.requiresMove) {
|
||||||
|
Ionic.listener.enable(this, 'document:mousemove', shouldEnable);
|
||||||
|
}
|
||||||
|
Ionic.listener.enable(this, 'document:mouseup', shouldEnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private enableTouch(shouldEnable: boolean) {
|
||||||
|
if (this.requiresMove) {
|
||||||
|
Ionic.listener.enable(this, 'touchmove', shouldEnable);
|
||||||
|
}
|
||||||
|
Ionic.listener.enable(this, 'touchend', shouldEnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private enable(shouldEnable: boolean) {
|
||||||
|
this.enableMouse(shouldEnable);
|
||||||
|
this.enableTouch(shouldEnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ionViewDidUnload() {
|
||||||
|
this.gesture && this.gesture.destroy();
|
||||||
|
this.gesture = this.pan = this.detail = this.detail.event = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const GESTURE_INLINE_STYLES = {
|
||||||
|
'touch-action': 'none',
|
||||||
|
'user-select': 'none',
|
||||||
|
'-webkit-user-drag': 'none',
|
||||||
|
'-webkit-tap-highlight-color': 'rgba(0,0,0,0)'
|
||||||
|
};
|
||||||
|
|
||||||
|
const MOUSE_WAIT = 2500;
|
||||||
|
|
||||||
|
|
||||||
|
function now(ev: UIEvent) {
|
||||||
|
return ev.timeStamp || Date.now();
|
||||||
|
}
|
||||||
|
|
||||||
66
src/components/gesture/recognizers.ts
Normal file
66
src/components/gesture/recognizers.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
|
||||||
|
export class PanRecognizer {
|
||||||
|
private startX: number;
|
||||||
|
private startY: number;
|
||||||
|
|
||||||
|
private dirty: boolean = false;
|
||||||
|
private threshold: number;
|
||||||
|
private maxCosine: number;
|
||||||
|
|
||||||
|
private angle = 0;
|
||||||
|
private isPan = 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(x: number, y: number) {
|
||||||
|
this.startX = x;
|
||||||
|
this.startY = y;
|
||||||
|
this.angle = 0;
|
||||||
|
this.isPan = 0;
|
||||||
|
this.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
detect(x: number, y: number): boolean {
|
||||||
|
if (!this.dirty) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deltaX = (x - this.startX);
|
||||||
|
const deltaY = (y - this.startY);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
isGesture(): number {
|
||||||
|
return this.isPan;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -16,7 +16,7 @@ $include-rtl: true !default;
|
|||||||
|
|
||||||
|
|
||||||
// Global font path
|
// Global font path
|
||||||
$font-path: "../fonts" !default;
|
$font-path: "/dist/fonts" !default;
|
||||||
|
|
||||||
|
|
||||||
// Ionicons font path
|
// Ionicons font path
|
||||||
|
|||||||
Reference in New Issue
Block a user