Files
pixel.horse/src/ts/client/input/touch.ts
T
2019-08-30 01:52:40 -07:00

204 lines
6.0 KiB
TypeScript

import { Key, InputController } from './input';
import { Point } from '../../common/interfaces';
import { setTransform } from '../../common/utils';
import { InputManager } from './inputManager';
function getTouch(e: TouchEvent, id: number) {
if (id !== -1) {
for (let i = 0; i < e.changedTouches.length; ++i) {
const touch = e.changedTouches.item(i);
if (touch && touch.identifier === id) {
return touch;
}
}
}
return undefined;
}
const TOUCH_DEADZONE = 15;
const TOUCH_MAX = 100;
export class TouchController implements InputController {
private initialized = false;
private touchId = -1;
private touch2Id = -1;
private touchStart: Point = { x: 0, y: 0 };
private touchCurrent: Point = { x: 0, y: 0 };
private touchIsDrag = false;
private tapInvalidated = false;
private origin?: HTMLElement;
private position?: HTMLElement;
private originShown = false;
private originTransform?: string;
private positionShown = false;
private positionTransform?: string;
private element?: HTMLElement;
constructor(private manager: InputManager) {
}
initialize(element: HTMLElement) {
if (!this.initialized) {
this.initialized = true;
this.element = element;
this.origin = document.getElementById('touch-origin')!;
this.position = document.getElementById('touch-position')!;
element.addEventListener('touchstart', this.touchstart);
element.addEventListener('touchmove', this.touchmove);
element.addEventListener('touchend', this.touchend);
window.addEventListener('touchend', this.blur);
window.addEventListener('blur', this.blur);
}
}
release() {
this.initialized = false;
if (this.element) {
this.element.removeEventListener('touchstart', this.touchstart);
this.element.removeEventListener('touchmove', this.touchmove);
this.element.removeEventListener('touchend', this.touchend);
this.element = undefined;
}
window.removeEventListener('touchend', this.blur);
window.removeEventListener('blur', this.blur);
}
update() {
const showOrigin = this.touchIsDrag && this.touchId !== -1;
const showPosition = this.touchId !== -1;
if (this.origin && this.position) {
if (this.originShown !== showOrigin) {
this.originShown = showOrigin;
this.origin.style.display = showOrigin ? 'block' : 'none';
}
if (this.positionShown !== showPosition) {
this.positionShown = showPosition;
this.position.style.display = showPosition ? 'block' : 'none';
}
if (showOrigin) {
const transform = `translate3d(${this.touchStart.x - 50}px, ${this.touchStart.y - 50}px, 0px)`;
if (this.originTransform !== transform) {
this.originTransform = transform;
setTransform(this.origin, transform);
}
}
if (showPosition) {
const transform = `translate3d(${this.touchCurrent.x - 25}px, ${this.touchCurrent.y - 25}px, 0px)`;
if (this.positionTransform !== transform) {
this.positionTransform = transform;
setTransform(this.position, transform);
}
}
}
}
clear() {
}
private reset() {
this.touch2Id = -1;
this.resetTouch();
}
private resetTouch() {
this.touchId = -1;
this.touchStart = this.touchCurrent = { x: 0, y: 0 };
this.touchIsDrag = false;
this.manager.setValue(Key.TOUCH, 0);
this.updateInput();
}
private updateInput() {
const dy = this.touchStart.y - this.touchCurrent.y;
const dx = this.touchStart.x - this.touchCurrent.x;
const theta = Math.atan2(dy, dx);
const dist = Math.sqrt(dy * dy + dx * dx);
if (dist > TOUCH_DEADZONE) {
const scaledDist = Math.min((dist - TOUCH_DEADZONE) / (TOUCH_MAX - TOUCH_DEADZONE), 1);
this.touchIsDrag = true;
this.manager.setValue(Key.GAMEPAD_AXIS1_X, -Math.cos(theta) * scaledDist);
this.manager.setValue(Key.GAMEPAD_AXIS1_Y, -Math.sin(theta) * scaledDist);
} else {
this.manager.setValue(Key.GAMEPAD_AXIS1_X, 0);
this.manager.setValue(Key.GAMEPAD_AXIS1_Y, 0);
}
}
private touchstart = (e: any) => {
e.cancellable && e.preventDefault();
e.stopPropagation();
this.manager.usingTouch = true;
if (this.touchId === -1) {
const touch = e.changedTouches.item(0);
if (touch) {
this.tapInvalidated = false;
this.touchId = touch.identifier;
this.touchStart = this.touchCurrent = this.getTouchXY(touch);
this.manager.setValue(Key.MOUSE_X, this.touchStart.x);
this.manager.setValue(Key.MOUSE_Y, this.touchStart.y);
this.manager.setValue(Key.TOUCH, 1);
}
} else if (this.touch2Id === -1) {
const touch = e.changedTouches.item(0);
if (touch) {
this.tapInvalidated = true;
this.touch2Id = touch.identifier;
}
}
}
private touchmove = (e: any) => {
e.preventDefault();
e.stopPropagation();
const touch = getTouch(e, this.touchId);
if (touch) {
this.touchCurrent = this.getTouchXY(touch);
this.manager.setValue(Key.MOUSE_X, this.touchCurrent.x);
this.manager.setValue(Key.MOUSE_Y, this.touchCurrent.y);
this.updateInput();
}
}
private touchend = (e: any) => {
e.preventDefault();
e.stopPropagation();
const touch = getTouch(e, this.touchId);
if (touch) {
if (!this.touchIsDrag && !this.tapInvalidated) {
this.manager.setValue(Key.MOUSE_X, this.touchStart.x);
this.manager.setValue(Key.MOUSE_Y, this.touchStart.y);
this.manager.setValue(Key.TOUCH_CLICK, 1);
}
this.resetTouch();
}
const touch2 = getTouch(e, this.touch2Id);
if (touch2) {
this.manager.setValue(Key.TOUCH_SECOND_CLICK, 1);
this.touch2Id = -1;
}
}
private blur = () => {
this.reset();
}
private getTouchXY(touch: Touch) {
const { left, top } = this.element!.getBoundingClientRect();
return {
x: touch.clientX - left,
y: touch.clientY - top,
};
}
}