tweb/src/components/swipeHandler.ts

164 lines
4.7 KiB
TypeScript

/*
* https://github.com/morethanwords/tweb
* Copyright (C) 2019-2021 Eduard Kuzmenko
* https://github.com/morethanwords/tweb/blob/master/LICENSE
*/
import { cancelEvent } from "../helpers/dom/cancelEvent";
import { safeAssign } from "../helpers/object";
import { IS_TOUCH_SUPPORTED } from "../environment/touchSupport";
import rootScope from "../lib/rootScope";
const getEvent = (e: TouchEvent | MouseEvent) => {
return (e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e as MouseEvent;
};
const attachGlobalListenerTo = window;
let RESET_GLOBAL = false;
rootScope.addEventListener('context_menu_toggle', (visible) => {
RESET_GLOBAL = visible;
});
export type SwipeHandlerOptions = {
element: SwipeHandler['element'],
onSwipe: SwipeHandler['onSwipe'],
verifyTouchTarget?: SwipeHandler['verifyTouchTarget'],
onFirstSwipe?: SwipeHandler['onFirstSwipe'],
onReset?: SwipeHandler['onReset'],
cursor?: SwipeHandler['cursor'],
cancelEvent?: SwipeHandler['cancelEvent'],
listenerOptions?: SwipeHandler['listenerOptions']
};
export default class SwipeHandler {
private element: HTMLElement;
private onSwipe: (xDiff: number, yDiff: number, e: TouchEvent | MouseEvent) => boolean | void;
private verifyTouchTarget: (evt: TouchEvent | MouseEvent) => boolean;
private onFirstSwipe: () => void;
private onReset: () => void;
private cursor: 'grabbing' | 'move' = 'grabbing';
private cancelEvent = true;
private listenerOptions: boolean | AddEventListenerOptions = false;
private hadMove = false;
private xDown: number = null;
private yDown: number = null;
constructor(options: SwipeHandlerOptions) {
safeAssign(this, options);
this.setListeners();
}
public setListeners() {
if(!IS_TOUCH_SUPPORTED) {
this.element.addEventListener('mousedown', this.handleStart, this.listenerOptions);
attachGlobalListenerTo.addEventListener('mouseup', this.reset);
} else {
this.element.addEventListener('touchstart', this.handleStart, this.listenerOptions);
attachGlobalListenerTo.addEventListener('touchend', this.reset);
}
}
public removeListeners() {
if(!IS_TOUCH_SUPPORTED) {
this.element.removeEventListener('mousedown', this.handleStart, this.listenerOptions);
attachGlobalListenerTo.removeEventListener('mouseup', this.reset);
} else {
this.element.removeEventListener('touchstart', this.handleStart, this.listenerOptions);
attachGlobalListenerTo.removeEventListener('touchend', this.reset);
}
}
reset = (e?: Event) => {
/* if(e) {
cancelEvent(e);
} */
if(IS_TOUCH_SUPPORTED) {
attachGlobalListenerTo.removeEventListener('touchmove', this.handleMove, {capture: true});
} else {
attachGlobalListenerTo.removeEventListener('mousemove', this.handleMove);
this.element.style.cursor = '';
}
if(this.onReset && this.hadMove) {
this.onReset();
}
this.xDown = this.yDown = null;
this.hadMove = false;
};
handleStart = (_e: TouchEvent | MouseEvent) => {
const e = getEvent(_e);
if(this.verifyTouchTarget && !this.verifyTouchTarget(_e)) {
return this.reset();
}
this.xDown = e.clientX;
this.yDown = e.clientY;
if(IS_TOUCH_SUPPORTED) {
attachGlobalListenerTo.addEventListener('touchmove', this.handleMove, {passive: false, capture: true});
} else {
attachGlobalListenerTo.addEventListener('mousemove', this.handleMove, false);
}
};
handleMove = (_e: TouchEvent | MouseEvent) => {
if(this.xDown === null || this.yDown === null || RESET_GLOBAL) {
this.reset();
return;
}
if(this.cancelEvent) {
cancelEvent(_e);
}
const e = getEvent(_e);
const xUp = e.clientX;
const yUp = e.clientY;
const xDiff = this.xDown - xUp;
const yDiff = this.yDown - yUp;
if(!this.hadMove) {
if(!xDiff && !yDiff) {
return;
}
this.hadMove = true;
if(!IS_TOUCH_SUPPORTED) {
this.element.style.setProperty('cursor', this.cursor, 'important');
}
if(this.onFirstSwipe) {
this.onFirstSwipe();
}
}
// if(Math.abs(xDiff) > Math.abs(yDiff)) { /*most significant*/
// if(xDiff > 0) { /* left swipe */
// } else { /* right swipe */
// }
// } else {
// if(yDiff > 0) { /* up swipe */
// } else { /* down swipe */
// }
// }
/* reset values */
const onSwipeResult = this.onSwipe(xDiff, yDiff, _e);
if(onSwipeResult !== undefined && onSwipeResult) {
this.reset();
}
};
}