From 555ecf8d808a1472a028501aa43d9843e1e1a3d2 Mon Sep 17 00:00:00 2001 From: Pihkaal Date: Fri, 13 Feb 2026 16:21:54 +0100 Subject: [PATCH] fix(nds): wrong touch detection --- app/components/NDS.vue | 61 +++++++++++++++++++++++++-------------- app/components/Screen.vue | 39 +++++++++++++------------ 2 files changed, 60 insertions(+), 40 deletions(-) diff --git a/app/components/NDS.vue b/app/components/NDS.vue index 8d262bf..38e8e22 100644 --- a/app/components/NDS.vue +++ b/app/components/NDS.vue @@ -326,26 +326,6 @@ const dispatchScreenEvent = ( ); }; -const dispatchScreenTouchEvent = ( - type: string, - canvas: HTMLCanvasElement, - intersection: THREE.Intersection, -) => { - const coords = toScreenCoords(canvas, intersection); - if (!coords) return; - - const touch = new Touch({ identifier: 0, target: canvas, ...coords }); - - canvas.dispatchEvent( - new TouchEvent(type, { - bubbles: true, - cancelable: true, - touches: type === "touchend" ? [] : [touch], - changedTouches: [touch], - }), - ); -}; - const BUTTON_MAP = { [X_BUTTON]: "X", [A_BUTTON]: "A", @@ -441,10 +421,40 @@ const handleMouseUp = (event: MouseEvent) => { if (canvas) dispatchScreenEvent("mouseup", canvas, intersection); }; +const SWIPE_THRESHOLD = 30; +let swipeStartX = 0; +let swipeStartY = 0; + +const dispatchSwipe = (endX: number, endY: number) => { + const deltaX = endX - swipeStartX; + const deltaY = endY - swipeStartY; + const absDeltaX = Math.abs(deltaX); + const absDeltaY = Math.abs(deltaY); + + if (Math.max(absDeltaX, absDeltaY) < SWIPE_THRESHOLD) return; + + const direction = + absDeltaX > absDeltaY + ? deltaX > 0 + ? "RIGHT" + : "LEFT" + : deltaY > 0 + ? "DOWN" + : "UP"; + + window.dispatchEvent( + new KeyboardEvent("keydown", { key: `NDS_SWIPE_${direction}` }), + ); +}; + const handleTouchStart = (event: TouchEvent) => { const touch = event.touches[0]; if (!touch) return; + event.preventDefault(); + swipeStartX = touch.clientX; + swipeStartY = touch.clientY; + if (!hasAnimated.value) { animateIntro(); return; @@ -452,7 +462,6 @@ const handleTouchStart = (event: TouchEvent) => { handleInteraction(touch.clientX, touch.clientY, (canvas, intersection) => { dispatchScreenEvent("mousedown", canvas, intersection); - dispatchScreenTouchEvent("touchstart", canvas, intersection); }); }; @@ -462,14 +471,22 @@ const handleTouchEnd = (event: TouchEvent) => { const touch = event.changedTouches[0]; if (!touch) return; + dispatchSwipe(touch.clientX, touch.clientY); + const intersection = raycast(touch.clientX, touch.clientY); if (!intersection?.uv) return; const canvas = getScreenCanvas(intersection.object.name); if (canvas) { dispatchScreenEvent("click", canvas, intersection); - dispatchScreenTouchEvent("touchend", canvas, intersection); } + + document.dispatchEvent( + new MouseEvent("mouseup", { + clientX: touch.clientX, + clientY: touch.clientY, + }), + ); }; onMounted(() => { diff --git a/app/components/Screen.vue b/app/components/Screen.vue index 14de880..8188d1e 100644 --- a/app/components/Screen.vue +++ b/app/components/Screen.vue @@ -33,16 +33,19 @@ const registerScreenMouseWheelCallback = ( return () => screenMouseWheelCallbacks.delete(callback); }; -const handleCanvasClick = (event: MouseEvent) => { - if (!canvas.value) return; - - const rect = canvas.value.getBoundingClientRect(); +const toLogicalCoords = (clientX: number, clientY: number) => { + const rect = canvas.value!.getBoundingClientRect(); const scaleX = LOGICAL_WIDTH / rect.width; const scaleY = LOGICAL_HEIGHT / rect.height; + return [ + (clientX - rect.left) * scaleX, + (clientY - rect.top) * scaleY, + ] as const; +}; - const x = (event.clientX - rect.left) * scaleX; - const y = (event.clientY - rect.top) * scaleY; - +const handleCanvasClick = (event: MouseEvent) => { + if (!canvas.value) return; + const [x, y] = toLogicalCoords(event.clientX, event.clientY); for (const callback of screenClickCallbacks) { callback(x, y); } @@ -50,14 +53,7 @@ const handleCanvasClick = (event: MouseEvent) => { const handleCanvasMouseDown = (event: MouseEvent) => { if (!canvas.value) return; - - const rect = canvas.value.getBoundingClientRect(); - const scaleX = LOGICAL_WIDTH / rect.width; - const scaleY = LOGICAL_HEIGHT / rect.height; - - const x = (event.clientX - rect.left) * scaleX; - const y = (event.clientY - rect.top) * scaleY; - + const [x, y] = toLogicalCoords(event.clientX, event.clientY); for (const callback of screenMouseDownCallbacks) { callback(x, y); } @@ -97,6 +93,7 @@ const handleTouchStart = (event: TouchEvent) => { const touch = event.touches[0]; if (!touch) return; + event.preventDefault(); swipeStartX = touch.clientX; swipeStartY = touch.clientY; @@ -113,6 +110,14 @@ const handleTouchEnd = (event: TouchEvent) => { if (!touch) return; dispatchSwipe(touch.clientX, touch.clientY); + + canvas.value?.dispatchEvent( + new MouseEvent("click", { + clientX: touch.clientX, + clientY: touch.clientY, + }), + ); + document.dispatchEvent( new MouseEvent("mouseup", { clientX: touch.clientX, @@ -188,9 +193,7 @@ onMounted(() => { canvas.value.addEventListener("click", handleCanvasClick); canvas.value.addEventListener("mousedown", handleCanvasMouseDown); canvas.value.addEventListener("wheel", handleCanvasWheel, { passive: true }); - canvas.value.addEventListener("touchstart", handleTouchStart, { - passive: true, - }); + canvas.value.addEventListener("touchstart", handleTouchStart); canvas.value.addEventListener("touchend", handleTouchEnd, { passive: true }); canvas.value.addEventListener("mousedown", handleSwipeMouseDown); canvas.value.addEventListener("mouseup", handleSwipeMouseUp);