@egjs/axes
Version:
A module used to change the information of user action entered by various input devices such as touch screen or mouse into the logical virtual coordinates. You can easily create a UI that responds to user actions.
195 lines (173 loc) • 5.38 kB
text/typescript
/*
* 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);
};
}