From f8aeeb5b4650bfa2aea3750ab508e8fdc81feb2b Mon Sep 17 00:00:00 2001 From: Tom Aisthorpe Date: Sat, 15 Jun 2024 19:48:32 +0100 Subject: [PATCH] feat(input): add single touch handling --- .changeset/lucky-jars-yell.md | 5 ++ packages/ted/src/index.ts | 1 + packages/ted/src/input/events.ts | 20 +++++++ packages/ted/src/input/touch.ts | 100 +++++++++++++++++++++++++++++++ 4 files changed, 126 insertions(+) create mode 100644 .changeset/lucky-jars-yell.md create mode 100644 packages/ted/src/input/touch.ts diff --git a/.changeset/lucky-jars-yell.md b/.changeset/lucky-jars-yell.md new file mode 100644 index 0000000..3194181 --- /dev/null +++ b/.changeset/lucky-jars-yell.md @@ -0,0 +1,5 @@ +--- +"@tedengine/ted": minor +--- + +Add basic touch handling diff --git a/packages/ted/src/index.ts b/packages/ted/src/index.ts index 31d25e5..5bc6b47 100644 --- a/packages/ted/src/index.ts +++ b/packages/ted/src/index.ts @@ -75,6 +75,7 @@ export * from './input/events'; export { default as TKeyboard } from './input/keyboard'; export { default as TMouse } from './input/mouse'; export * from './input/mouse'; +export { default as TTouch } from './input/touch'; export { default as TSimpleController } from './input/simple-controller'; export { default as TTransform } from './math/transform'; diff --git a/packages/ted/src/input/events.ts b/packages/ted/src/input/events.ts index 6cf6098..625d71b 100644 --- a/packages/ted/src/input/events.ts +++ b/packages/ted/src/input/events.ts @@ -6,6 +6,10 @@ export enum TEventTypesInput { MouseUp = 'mouseup', MouseDown = 'mousedown', MouseMove = 'mousemove', + TouchStart = 'touchstart', + TouchEnd = 'touchend', + TouchMove = 'touchmove', + TouchCancel = 'touchcancel', ActionPressed = 'controller_actionpressed', ActionReleased = 'controller_actionreleased', } @@ -40,6 +44,22 @@ export interface TMouseMoveEvent extends TMouseLocation { type: TEventTypesInput.MouseMove; } +export interface TTouchStartEvent extends TMouseLocation { + type: TEventTypesInput.TouchStart; +} + +export interface TTouchEndEvent extends TMouseLocation { + type: TEventTypesInput.TouchEnd; +} + +export interface TTouchMoveEvent extends TMouseLocation { + type: TEventTypesInput.TouchMove; +} + +export interface TTouchCancelEvent extends TMouseLocation { + type: TEventTypesInput.TouchCancel; +} + export interface TActionPressedEvent { type: TEventTypesInput.ActionPressed; subType: string; // Action pressed diff --git a/packages/ted/src/input/touch.ts b/packages/ted/src/input/touch.ts new file mode 100644 index 0000000..cadb20a --- /dev/null +++ b/packages/ted/src/input/touch.ts @@ -0,0 +1,100 @@ +import { vec2 } from 'gl-matrix'; +import type TEventQueue from '../core/event-queue'; +import type { + TMouseLocation, + TTouchCancelEvent, + TTouchEndEvent, + TTouchMoveEvent, + TTouchStartEvent, +} from './events'; +import { TEventTypesInput } from './events'; + +export default class TMouse { + private touchMoveListener: (e: TouchEvent) => void; + private touchStartListener: (e: TouchEvent) => void; + private touchEndListener: (e: TouchEvent) => void; + private touchCancelListener: (e: TouchEvent) => void; + + constructor( + eventQueue: TEventQueue, + private canvas: HTMLCanvasElement, + ) { + this.touchMoveListener = (e) => this.handleTouchMove(e, eventQueue); + this.touchMoveListener = this.touchMoveListener.bind(this); + window.addEventListener('touchmove', this.touchMoveListener); + + this.touchStartListener = (e) => this.handleTouchStart(e, eventQueue); + this.touchStartListener = this.touchStartListener.bind(this); + this.canvas.addEventListener('touchstart', this.touchStartListener); + + this.touchEndListener = (e) => this.handleTouchEnd(e, eventQueue); + this.touchEndListener = this.touchEndListener.bind(this); + this.canvas.addEventListener('touchend', this.touchEndListener); + + this.touchCancelListener = (e) => this.handleTouchCancel(e, eventQueue); + this.touchCancelListener = this.touchCancelListener.bind(this); + window.addEventListener('touchcancel', this.touchCancelListener); + } + + public destroy() { + window.removeEventListener('touchmove', this.touchMoveListener); + window.removeEventListener('touchcancel', this.touchCancelListener); + this.canvas.removeEventListener('touchstart', this.touchStartListener); + this.canvas.removeEventListener('touchend', this.touchEndListener); + } + + private handleTouchMove(e: TouchEvent, eventQueue: TEventQueue) { + const event: TTouchMoveEvent = { + type: TEventTypesInput.TouchMove, + ...this.getTouchLocation(e), + }; + + eventQueue.broadcast(event); + } + + private handleTouchStart(e: TouchEvent, eventQueue: TEventQueue) { + const event: TTouchStartEvent = { + type: TEventTypesInput.TouchStart, + ...this.getTouchLocation(e), + }; + + eventQueue.broadcast(event); + } + + private handleTouchEnd(e: TouchEvent, eventQueue: TEventQueue) { + const event: TTouchEndEvent = { + type: TEventTypesInput.TouchEnd, + ...this.getTouchLocation(e), + }; + + eventQueue.broadcast(event); + } + + private handleTouchCancel(e: TouchEvent, eventQueue: TEventQueue) { + const event: TTouchCancelEvent = { + type: TEventTypesInput.TouchCancel, + ...this.getTouchLocation(e), + }; + + eventQueue.broadcast(event); + } + + private getTouchLocation(e: TouchEvent) { + const offset = this.canvas.getBoundingClientRect(); + + const touch = e.touches[0]; + const result: TMouseLocation = { + client: vec2.fromValues(touch.clientX, touch.clientY), + screen: vec2.fromValues( + touch.clientX - offset.left, + touch.clientY - offset.top, + ), + clip: vec2.fromValues( + ((touch.clientX - offset.left) / this.canvas.clientWidth) * 2 - 1, + ((touch.clientY - offset.top) / this.canvas.clientHeight) * -2 + 1, + ), + }; + + return result; + } +}