mirror of
https://github.com/Terncode/pixel.horse.git
synced 2026-09-25 22:25:53 +02:00
204 lines
6.0 KiB
TypeScript
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,
|
|
};
|
|
}
|
|
}
|