From e593efafa4e1045766001cf817881ed5a6026af0 Mon Sep 17 00:00:00 2001 From: Moonlit Productions Date: Mon, 10 Nov 2025 14:18:03 -0500 Subject: [PATCH] Added touch events, joysticks now work on mobile --- index.html | 2 +- script.js | 96 +++++++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 89 insertions(+), 9 deletions(-) diff --git a/index.html b/index.html index f6d9909..a6527c6 100644 --- a/index.html +++ b/index.html @@ -199,7 +199,7 @@ if scale < 1: Translation & Rotation
- +
diff --git a/script.js b/script.js index 56be202..c6aef35 100644 --- a/script.js +++ b/script.js @@ -24,13 +24,13 @@ class Joystick { this.ctx.beginPath(); this.ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2); - this.ctx.fillStyle = '#5554'; + this.ctx.fillStyle = rootStyles.getPropertyValue('--primary-dark-blue'); this.ctx.fill(); this.ctx.beginPath(); this.ctx.arc(this.nubX, this.nubY, this.radius / 3, 0, Math.PI * 2); - this.ctx.fillStyle = '#445bcaff'; + this.ctx.fillStyle = rootStyles.getPropertyValue('--accent-blue'); this.ctx.fill(); this.ctx.restore(); @@ -61,6 +61,11 @@ class Joystick { } } + reset() { + this.nubX = this.x; + this.nubY = this.y; + } + getX() { return (this.nubX - this.x) / this.radius; } @@ -464,7 +469,7 @@ controlModeToggle.addEventListener('click', () => { // Switch to slider mode sliderControls.style.display = 'block'; keyboardControls.style.display = 'none'; - controlModeToggle.textContent = 'Switch to Keyboard Controls (WASD + QE)'; + controlModeToggle.textContent = 'Switch to Keyboard/Joystick Controls'; // Reset manual input state Object.keys(keyState).forEach(key => keyState[key] = false); @@ -637,8 +642,15 @@ applyCustomBtn.addEventListener('click', () => { function convertTouchToCanvas(inCoords) { const rect = canvas.getBoundingClientRect(); - const x = inCoords.x - rect.left - canvas.width / 2; - const y = inCoords.y - rect.top - canvas.height / 2; + + // Calculate the scale factor between display size and canvas internal size + const scaleX = canvas.width / rect.width; + const scaleY = canvas.height / rect.height; + + // Convert client coordinates to canvas coordinates, accounting for scaling + const x = (inCoords.x - rect.left) * scaleX - canvas.width / 2; + const y = (inCoords.y - rect.top) * scaleY - canvas.height / 2; + return { x, y }; } @@ -659,6 +671,74 @@ canvas.addEventListener('mouseup', (event) => { rightJoystick.deactivate(); }); +// Touch event listeners for mobile/tablet support +canvas.addEventListener('touchstart', (event) => { + event.preventDefault(); // Prevent scrolling and default touch behavior + + for (let i = 0; i < event.touches.length; i++) { + const touch = event.touches[i]; + const canvasCoords = convertTouchToCanvas({ x: touch.clientX, y: touch.clientY }); + // alert(`X: ${canvasCoords.x}, Y: ${canvasCoords.y}`); + leftJoystick.checkShouldActivate(canvasCoords.x, canvasCoords.y); + rightJoystick.checkShouldActivate(canvasCoords.x, canvasCoords.y); + } +}); + +canvas.addEventListener('touchmove', (event) => { + event.preventDefault(); // Prevent scrolling while using joysticks + + for (let i = 0; i < event.touches.length; i++) { + const touch = event.touches[i]; + const canvasCoords = convertTouchToCanvas({ x: touch.clientX, y: touch.clientY }); + leftJoystick.processTouch(canvasCoords.x, canvasCoords.y); + rightJoystick.processTouch(canvasCoords.x, canvasCoords.y); + } +}); + +canvas.addEventListener('touchend', (event) => { + event.preventDefault(); + + // If no touches remain, deactivate both joysticks + if (event.touches.length === 0) { + leftJoystick.deactivate(); + rightJoystick.deactivate(); + } else { + // Check if the remaining touches are still in range of the joysticks + let leftStillActive = false; + let rightStillActive = false; + + for (let i = 0; i < event.touches.length; i++) { + const touch = event.touches[i]; + const canvasCoords = convertTouchToCanvas({ x: touch.clientX, y: touch.clientY }); + + if (leftJoystick.touchInRange(canvasCoords.x, canvasCoords.y)) { + leftStillActive = true; + } + if (rightJoystick.touchInRange(canvasCoords.x, canvasCoords.y)) { + rightStillActive = true; + } + } + + if (!leftStillActive) { + leftJoystick.deactivate(); + leftJoystick.reset(); + } + if (!rightStillActive) { + rightJoystick.deactivate(); + rightJoystick.reset(); + } + } +}); + +canvas.addEventListener('touchcancel', (event) => { + // Handle touch cancellation (e.g., when system interrupts) + leftJoystick.deactivate(); + leftJoystick.reset(); + rightJoystick.deactivate(); + rightJoystick.reset(); +}); + + /* * END LISTENER CODE * BEGIN DYNAMIC DOM FUNCTIONS @@ -934,9 +1014,6 @@ function animate() { ctx.save(); ctx.translate(canvas.width / 2, canvas.height / 2); - leftJoystick.draw(); - rightJoystick.draw(); - // Update speeds based on control mode if (isManualInputMode) { const maxSpeed = parseFloat(keyboardMaxSpeed.value); @@ -986,6 +1063,9 @@ function animate() { drawGrid(ctx, canvas.width * 2, gridSquareSize, xGridOffset, yGridOffset); drawRobot(ctx, robot, robot.gyroHeading); + leftJoystick.draw(); + rightJoystick.draw(); + // Do it all over again ctx.restore(); requestAnimationFrame(animate);