diff --git a/src/main.ts b/src/main.ts index 8cbbd67..74a608f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,6 @@ import "./style.css" -import { TouchManager } from "./touch-manager"; +import { TouchManager, type Finger } from "./touch-manager"; import { SpiceConnection } from "./spice-connection"; import { listenHold } from "./listen-hold"; @@ -108,7 +108,7 @@ const lanes = Array.from(document.querySelectorAll(".lane>div")) const faderPositions = Array.from(document.querySelectorAll(".fader .position")); const laneState = new Array(12).fill(false); -const faderTouches: Array = [null, null]; +const faderTouches: Array = [null, null]; const lastFaderPositions: Array = [null, null]; const faderDirs = [0, 0]; const faderAnalogs = [0.51, 0.51]; @@ -165,102 +165,60 @@ function updateFaderAnalog() { } } -let laneTouches: number[][] = new Array(12).fill([]); -function updateLaneState() { - for (let i = 0; i < 12; i++) { - laneState[i] = !!laneTouches[i].length; - } -} +window.touchMgr.onTouchStart = (finger) => { + const x = finger.x / document.body.clientWidth; + const y = finger.y / document.body.clientHeight; -function removeTouches(touches: Touch[]): boolean { - // remove ended touches - let changed = false; - - // remove changed touches - for (let i = 0; i < 12; i++) { - const oldLen = laneTouches[i].length; - laneTouches[i] = - laneTouches[i].filter(v => touches.findIndex(t => t.identifier == v) == -1); - - if (oldLen != laneTouches[i].length) { - changed = true; - } + if (y > 0.35) { + return; } - return changed -} + // find new touch for binding to fader + if (faderTouches[1] && finger.x < faderTouches[1].x) { + faderTouches[0] = finger; + } else if (x < 0.5 && !faderTouches[0]) { + faderTouches[0] = finger; + } -window.touchMgr.onTouchStart = (touches) => { - const touchArr = Array.from(touches); - - for (const touch of touchArr) { - const x = touch.clientX / document.body.clientWidth; - const y = touch.clientY / document.body.clientHeight; - - if (y > 0.5) { - continue; - } - - // find new touch for binding to fader - if (faderTouches[1] && touch.clientX < faderTouches[1].clientX) { - faderTouches[0] = touch; - } else if (x < 0.5 && !faderTouches[0]) { - faderTouches[0] = touch; - } - - if (faderTouches[0] && touch.clientX > faderTouches[0].clientX) { - faderTouches[1] = touch; - } else if (x > 0.5 && !faderTouches[1]) { - faderTouches[1] = touch; - } + if (faderTouches[0] && finger.x > faderTouches[0].x) { + faderTouches[1] = finger; + } else if (x > 0.5 && !faderTouches[1]) { + faderTouches[1] = finger; } }; -window.touchMgr.onTouchChange = (touches, noSend) => { - const touchArr = Array.from(touches); - let changed = removeTouches(touchArr); +window.touchMgr.onTouchChange = (fingers) => { + laneState.fill(false); - for (const touch of touchArr) { - const x = touch.clientX / document.body.clientWidth; - const y = touch.clientY / document.body.clientHeight; + for (const finger of fingers) { + const x = finger.x / document.body.clientWidth; + const y = finger.y / document.body.clientHeight; // update touch bounded to fader - if (touch.identifier == faderTouches[0]?.identifier) { - faderTouches[0] = touch; + if (finger.id == faderTouches[0]?.id) { + faderTouches[0] = finger; continue; } - if (touch.identifier == faderTouches[1]?.identifier) { - faderTouches[1] = touch; + if (finger.id == faderTouches[1]?.id) { + faderTouches[1] = finger; continue; } // lane area touches - if (y < 0.5) { + if (y < 0.35) { continue; } const column = Math.max(0, Math.min(Math.floor(x * 12), 11)); - laneTouches[column].push(touch.identifier); - changed = true; - } - - if (changed) { - updateLaneState(); - if (!noSend) sendButtonState(laneState); + laneState[column] = true; } + sendButtonState(laneState); frames++; }; -window.touchMgr.onTouchEnd = (touches) => { - const touchArr = Array.from(touches); - const changed = removeTouches(touchArr); - if (changed) { - updateLaneState(); - sendButtonState(laneState); - } - +window.touchMgr.onTouchEnd = (finger) => { // remove fader touches for (let i = 0; i < 2; i++) { const faderTouch = faderTouches[i]; @@ -268,19 +226,15 @@ window.touchMgr.onTouchEnd = (touches) => { continue } - for (const touch of touchArr) { - if (faderTouch.identifier !== touch.identifier) { - continue - } - - faderTouches[i] = null; - lastFaderPositions[i] = null; - faderDirs[i] = 0; - break; + if (faderTouch.id !== finger.id) { + return } - } - frames++; + faderTouches[i] = null; + lastFaderPositions[i] = null; + faderDirs[i] = 0; + break; + } }; const update = () => { @@ -303,7 +257,7 @@ const update = () => { continue; } - const x = faderTouch.clientX - (i * areaWidth); + const x = faderTouch.x - (i * areaWidth); const lastX = lastFaderPositions[i]; if (lastX !== null) { const delta = x - lastX; diff --git a/src/style.css b/src/style.css index 2c1934a..08d8f9d 100644 --- a/src/style.css +++ b/src/style.css @@ -16,7 +16,7 @@ body { .fader { display: flex; - height: 50vh; + height: 35vh; } .fader>div { @@ -57,7 +57,7 @@ body { .lane { display: flex; background-color: #000; - height: 50vh; + height: 65vh; } .lane>div { diff --git a/src/touch-manager.ts b/src/touch-manager.ts index 03057ce..805e810 100644 --- a/src/touch-manager.ts +++ b/src/touch-manager.ts @@ -1,119 +1,78 @@ -export type TouchEventCallback = (touches: ArrayLike, noSend?: boolean) => void; -export class TouchManager { - onTouchEnd: TouchEventCallback | undefined = undefined; - onTouchChange: TouchEventCallback | undefined = undefined; - onTouchStart: TouchEventCallback | undefined = undefined; +export interface Finger { + id: number | string; + x: number; + y: number; +} - touchStartTimes: Map = new Map(); +export class TouchManager { + onTouchStart: ((touch: Finger) => void) | undefined = undefined; + onTouchChange: ((touches: Finger[]) => void) | undefined = undefined; + onTouchEnd: ((touches: Finger) => void) | undefined = undefined; + + touches: Map = new Map(); constructor() { - document.addEventListener("touchmove", (e) => { - if (this.onTouchChange) { - this.onTouchChange(e.changedTouches); - } - e.preventDefault(); - }); - - document.addEventListener("touchstart", (e) => { - for (const touch of e.changedTouches) { - this.touchStartTimes.set(touch.identifier, Date.now()); - } + document.addEventListener("pointerdown", (e) => { + const finger = { + id: e.pointerId, + x: e.clientX, + y: e.clientY, + }; if (this.onTouchStart) { - this.onTouchStart(e.changedTouches); + this.onTouchStart(finger); } + this.touches.set(e.pointerId, finger); + const touches = Array.from(this.touches.values()); + if (this.onTouchChange) { - this.onTouchChange(e.changedTouches); + this.onTouchChange(touches); } + e.preventDefault(); }); - document.addEventListener("touchend", (e) => { - this.touchEnd(e.changedTouches); - e.preventDefault(); - }); - - document.addEventListener("touchcancel", (e) => { - this.touchEnd(e.changedTouches); - e.preventDefault(); - }); - - document.addEventListener("mousedown", (e) => { - const fakeTouch = { - clientX: e.clientX, - clientY: e.clientY, - identifier: 'MOUSE' as any, - } as Touch; - - this.touchStartTimes.set(fakeTouch.identifier, Date.now()); - if (this.onTouchStart) { - this.onTouchStart([fakeTouch]); - } - - if (this.onTouchChange) { - this.onTouchChange([fakeTouch]); - } - }); - - document.addEventListener("mousemove", (e) => { - if (!this.touchStartTimes.has('MOUSE')) { + document.addEventListener("pointermove", (e) => { + if (!this.touches.has(e.pointerId)) { return; } - const fakeTouch = { - clientX: e.clientX, - clientY: e.clientY, - identifier: 'MOUSE' as any, - } as Touch; + this.touches.set(e.pointerId, { + id: e.pointerId, + x: e.clientX, + y: e.clientY, + }); + + const touches = Array.from(this.touches.values()); if (this.onTouchChange) { - this.onTouchChange([fakeTouch]); + this.onTouchChange(touches); } + + e.preventDefault(); }); - document.addEventListener("mouseup", (e) => { - const fakeTouch = { - clientX: e.clientX, - clientY: e.clientY, - identifier: 'MOUSE' as any, - } as Touch; - - this.touchEnd([fakeTouch]); + document.addEventListener("pointerup", (e) => { + this.touchEnd({ + id: e.pointerId, + x: e.clientX, + y: e.clientY, + }); }); } - touchEnd(changedTouches: TouchList | Touch[]) { + touchEnd(finger: Finger) { + this.touches.delete(finger.id); + + const touches = Array.from(this.touches.values()); + if (this.onTouchChange) { - this.onTouchChange(changedTouches, true); + this.onTouchChange(touches); } - const instaRelease = []; - - for (const touch of changedTouches) { - const startTime = this.touchStartTimes.get(touch.identifier); - if (startTime == undefined) { - throw new Error("?"); - } - - const duration = Date.now() - startTime; - this.touchStartTimes.delete(touch.identifier); - - // delay touch end event if it releases too fast - if (duration > 16) { - instaRelease.push(touch); - continue; - } - - setTimeout(() => { - if (this.onTouchEnd) { - this.onTouchEnd([touch]); - } - }, 16 - duration); - } - - if (this.onTouchEnd && instaRelease.length) { - this.onTouchEnd(instaRelease); + if (this.onTouchEnd) { + this.onTouchEnd(finger); } } }