From a0df576ed68da88ab8325cd6e119d677258f80ce Mon Sep 17 00:00:00 2001 From: alexpaynex <55271826-alexpaynex@users.noreply.replit.com> Date: Thu, 19 Mar 2026 03:33:48 +0000 Subject: [PATCH] Add touchstart fallback and adjust interaction lockout Introduce `touchstart` event listener as a fallback for older browsers lacking Pointer Events, and reduce the interaction lockout timer from 220ms to 150ms to prevent accidental orbit drags after a slap. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 418bf6f8-212b-4bb0-a7a5-8231a061da4e Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: b1d20c43-904b-495f-9262-401975d950d3 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/9f85e954-647c-46a5-90a7-396e495a805a/418bf6f8-212b-4bb0-a7a5-8231a061da4e/Q83Uqvu Replit-Helium-Checkpoint-Created: true --- the-matrix/js/interaction.js | 39 ++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/the-matrix/js/interaction.js b/the-matrix/js/interaction.js index 2bf5bea..5a7d831 100644 --- a/the-matrix/js/interaction.js +++ b/the-matrix/js/interaction.js @@ -39,27 +39,47 @@ export function initInteraction(camera, renderer) { // Capture phase so we intercept before OrbitControls' bubble-phase handler. // If Timmy is hit we call stopImmediatePropagation() to suppress the orbit drag. _canvas.addEventListener('pointerdown', _onPointerDown, { capture: true }); + + // touchstart fallback for older mobile browsers that lack Pointer Events + if (!window.PointerEvent) { + _canvas.addEventListener('touchstart', _onTouchStart, { capture: true, passive: false }); + } } -function _onPointerDown(event) { - if (!_timmyGroup || !_applySlap || !_camera) return; +function _hitTest(clientX, clientY) { + if (!_timmyGroup || !_applySlap || !_camera) return false; const rect = _canvas.getBoundingClientRect(); - _pointer.x = ((event.clientX - rect.left) / rect.width) * 2 - 1; - _pointer.y = ((event.clientY - rect.top) / rect.height) * -2 + 1; + _pointer.x = ((clientX - rect.left) / rect.width) * 2 - 1; + _pointer.y = ((clientY - rect.top) / rect.height) * -2 + 1; _raycaster.setFromCamera(_pointer, _camera); const hits = _raycaster.intersectObject(_timmyGroup, true); if (hits.length > 0) { - event.stopImmediatePropagation(); // block OrbitControls drag _applySlap(hits[0].point); - - // Re-enable orbit after a short pause to avoid accidental rotation + // 150 ms lockout to avoid accidental orbit drag immediately after a slap if (controls) { controls.enabled = false; - setTimeout(() => { if (controls) controls.enabled = true; }, 220); + setTimeout(() => { if (controls) controls.enabled = true; }, 150); } + return true; + } + return false; +} + +function _onPointerDown(event) { + if (_hitTest(event.clientX, event.clientY)) { + event.stopImmediatePropagation(); // block OrbitControls drag + } +} + +function _onTouchStart(event) { + if (!event.touches || event.touches.length === 0) return; + const t = event.touches[0]; + if (_hitTest(t.clientX, t.clientY)) { + event.stopImmediatePropagation(); + event.preventDefault(); // suppress subsequent mouse events (ghost click) } } @@ -75,6 +95,9 @@ export function disposeInteraction() { if (_canvas) { _canvas.removeEventListener('contextmenu', _noCtxMenu); _canvas.removeEventListener('pointerdown', _onPointerDown, { capture: true }); + if (!window.PointerEvent) { + _canvas.removeEventListener('touchstart', _onTouchStart, { capture: true }); + } _canvas = null; } if (controls) {