/* * Copyright (c) 2015 NAVER Corp. * egjs projects are licensed under the MIT license */ import { ExtendedEvent, InputEventType, LatestInterval } from "../types"; import { getAngle } from "../utils"; import { window } from "../browser"; import { ALT, ANY, CTRL, META, MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT, NONE, SHIFT, VELOCITY_INTERVAL, } from "../const"; export const SUPPORT_TOUCH = "ontouchstart" in window; export const SUPPORT_POINTER = "PointerEvent" in window; export const SUPPORT_MSPOINTER = "MSPointerEvent" in window; export const SUPPORT_POINTER_EVENTS = SUPPORT_POINTER || SUPPORT_MSPOINTER; export const isValidKey = ( event: InputEventType | WheelEvent, inputKey?: string[] ): boolean => { if ( !inputKey || inputKey.indexOf(ANY) > -1 || (inputKey.indexOf(NONE) > -1 && !event.shiftKey && !event.ctrlKey && !event.altKey && !event.metaKey) || (inputKey.indexOf(SHIFT) > -1 && event.shiftKey) || (inputKey.indexOf(CTRL) > -1 && event.ctrlKey) || (inputKey.indexOf(ALT) > -1 && event.altKey) || (inputKey.indexOf(META) > -1 && event.metaKey) ) { return true; } return false; }; export abstract class EventInput { public prevEvent: ExtendedEvent; private _latestInterval: LatestInterval; public abstract onEventStart( event: InputEventType, inputButton?: string[] ): ExtendedEvent; public abstract onEventMove( event: InputEventType, inputButton?: string[] ): ExtendedEvent; public abstract onEventEnd(event: InputEventType): void; public abstract onRelease(event: InputEventType): void; public abstract getTouches( event: InputEventType, inputKey?: string[], inputButton?: string[] ): number; protected abstract _getScale(event: InputEventType): number; protected abstract _getCenter(event: InputEventType): { x: number; y: number; }; protected abstract _getMovement(event: InputEventType): { x: number; y: number; }; public extendEvent(event: InputEventType): ExtendedEvent { const prevEvent = this.prevEvent; const center = this._getCenter(event); const movement = prevEvent ? this._getMovement(event) : { x: 0, y: 0 }; const scale = prevEvent ? this._getScale(event) : 1; const angle = prevEvent ? getAngle(center.x - prevEvent.center.x, center.y - prevEvent.center.y) : 0; const deltaX = prevEvent ? prevEvent.deltaX + movement.x : movement.x; const deltaY = prevEvent ? prevEvent.deltaY + movement.y : movement.y; const offsetX = movement.x; const offsetY = movement.y; const latestInterval = this._latestInterval; const timeStamp = Date.now(); const deltaTime = latestInterval ? timeStamp - latestInterval.timestamp : 0; let velocityX = prevEvent ? prevEvent.velocityX : 0; let velocityY = prevEvent ? prevEvent.velocityY : 0; let directionX = prevEvent ? prevEvent.directionX : 1; let directionY = prevEvent ? prevEvent.directionY : 1; // If offset is 0, it inherits the direction of the previous event. if (offsetX > 0) { directionX = 1; } else if (offsetX < 0) { directionX = -1; } if (offsetY > 0) { directionY = 1; } else if (offsetY < 0) { directionY = -1; } if (!latestInterval || deltaTime >= VELOCITY_INTERVAL) { if (latestInterval) { [velocityX, velocityY] = [ (deltaX - latestInterval.deltaX) / deltaTime, (deltaY - latestInterval.deltaY) / deltaTime, ]; } this._latestInterval = { timestamp: timeStamp, deltaX, deltaY, }; } return { srcEvent: event, scale, angle, center, deltaX, deltaY, offsetX, offsetY, directionX, directionY, velocityX, velocityY, preventSystemEvent: true, }; } protected _getDistance( start: Touch | PointerEvent, end: Touch | PointerEvent ): number { const x = end.clientX - start.clientX; const y = end.clientY - start.clientY; return Math.sqrt(x * x + y * y); } protected _getButton(event: InputEventType): string { const buttonCodeMap = { 1: MOUSE_LEFT, 2: MOUSE_RIGHT, 4: MOUSE_MIDDLE }; const button = this._isTouchEvent(event) ? MOUSE_LEFT : buttonCodeMap[event.buttons]; return button ? button : null; } protected _isTouchEvent(event: InputEventType): event is TouchEvent { return event.type && event.type.indexOf("touch") > -1; } protected _isValidButton(button: string, inputButton: string[]): boolean { return inputButton.indexOf(button) > -1; } protected _isValidEvent( event: InputEventType, inputKey?: string[], inputButton?: string[] ): boolean { return ( (!inputKey || isValidKey(event, inputKey)) && (!inputButton || this._isValidButton(this._getButton(event), inputButton)) ); } protected _preventMouseButton(event: InputEventType, button: string): void { if (button === MOUSE_RIGHT) { window.addEventListener("contextmenu", this._stopContextMenu); } else if (button === MOUSE_MIDDLE) { event.preventDefault(); } } private _stopContextMenu = (event: InputEventType) => { event.preventDefault(); window.removeEventListener("contextmenu", this._stopContextMenu); }; }