diff --git a/index.html b/index.html index 56cd863..3a49f89 100644 --- a/index.html +++ b/index.html @@ -73,6 +73,8 @@

🖱️ Left click + drag: Rotate around model

🖱️ Right click + drag: Pan

⚙️ Mouse wheel: Zoom in/out

+

📱 Touch drag: Rotate around model

+

🤏 Pinch: Zoom in/out

@@ -90,6 +92,17 @@ let scene, camera, renderer, controls; let cube; + // Touch controls variables + let touchState = { + touching: false, + touchCount: 0, + lastTouches: [], + initialDistance: 0, + lastDistance: 0, + rotationStart: { x: 0, y: 0 }, + lastRotation: { x: 0, y: 0 } + }; + // Initialize the 3D scene function init() { // Create container @@ -135,6 +148,9 @@ controls.enablePan = true; controls.enableRotate = true; + // Setup touch controls + setupTouchControls(renderer.domElement); + // Add lighting setupLighting(); @@ -218,6 +234,133 @@ window.environmentMap = texture; } + function setupTouchControls(domElement) { + // Touch start event + domElement.addEventListener('touchstart', function(event) { + event.preventDefault(); + + touchState.touching = true; + touchState.touchCount = event.touches.length; + touchState.lastTouches = Array.from(event.touches); + + if (event.touches.length === 1) { + // Single touch - prepare for rotation + const touch = event.touches[0]; + touchState.rotationStart.x = touch.clientX; + touchState.rotationStart.y = touch.clientY; + touchState.lastRotation.x = touch.clientX; + touchState.lastRotation.y = touch.clientY; + + // Disable OrbitControls to prevent conflicts + controls.enabled = false; + } else if (event.touches.length === 2) { + // Two touches - prepare for pinch zoom + const touch1 = event.touches[0]; + const touch2 = event.touches[1]; + + touchState.initialDistance = getTouchDistance(touch1, touch2); + touchState.lastDistance = touchState.initialDistance; + + // Disable OrbitControls to prevent conflicts + controls.enabled = false; + } + }, false); + + // Touch move event + domElement.addEventListener('touchmove', function(event) { + event.preventDefault(); + + if (!touchState.touching) return; + + if (event.touches.length === 1 && touchState.touchCount === 1) { + // Single touch drag - rotation + const touch = event.touches[0]; + const deltaX = touch.clientX - touchState.lastRotation.x; + const deltaY = touch.clientY - touchState.lastRotation.y; + + // Apply rotation to the controls + const rotationSpeed = 0.005; + controls.enabled = true; // Temporarily enable to apply rotation + + // Simulate mouse movement for OrbitControls + const spherical = new THREE.Spherical(); + spherical.setFromVector3(camera.position.clone().sub(controls.target)); + + spherical.theta -= deltaX * rotationSpeed; + spherical.phi += deltaY * rotationSpeed; + + // Constrain phi to prevent camera flipping + spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi)); + + const newPosition = new THREE.Vector3(); + newPosition.setFromSpherical(spherical).add(controls.target); + camera.position.copy(newPosition); + camera.lookAt(controls.target); + + controls.enabled = false; // Disable again to prevent conflicts + + touchState.lastRotation.x = touch.clientX; + touchState.lastRotation.y = touch.clientY; + } else if (event.touches.length === 2) { + // Two touches - pinch zoom + const touch1 = event.touches[0]; + const touch2 = event.touches[1]; + const currentDistance = getTouchDistance(touch1, touch2); + + const scaleFactor = currentDistance / touchState.lastDistance; + + // Apply zoom + const zoomSpeed = 0.1; + const direction = camera.position.clone().sub(controls.target).normalize(); + const currentDistance3D = camera.position.distanceTo(controls.target); + + let newDistance; + if (scaleFactor > 1) { + // Pinching out - zoom in + newDistance = Math.max(1, currentDistance3D * (1 - zoomSpeed * (scaleFactor - 1))); + } else { + // Pinching in - zoom out + newDistance = currentDistance3D * (1 + zoomSpeed * (1 - scaleFactor)); + } + + camera.position.copy(controls.target.clone().add(direction.multiplyScalar(newDistance))); + + touchState.lastDistance = currentDistance; + } + }, false); + + // Touch end event + domElement.addEventListener('touchend', function(event) { + event.preventDefault(); + + touchState.touchCount = event.touches.length; + + if (event.touches.length === 0) { + // No more touches - re-enable OrbitControls + touchState.touching = false; + controls.enabled = true; + } else if (event.touches.length === 1 && touchState.touchCount > 1) { + // Switched from multi-touch to single touch + const touch = event.touches[0]; + touchState.rotationStart.x = touch.clientX; + touchState.rotationStart.y = touch.clientY; + touchState.lastRotation.x = touch.clientX; + touchState.lastRotation.y = touch.clientY; + } + }, false); + + // Prevent context menu on long press + domElement.addEventListener('contextmenu', function(event) { + event.preventDefault(); + }, false); + } + + function getTouchDistance(touch1, touch2) { + const dx = touch1.clientX - touch2.clientX; + const dy = touch1.clientY - touch2.clientY; + return Math.sqrt(dx * dx + dy * dy); + } + function loadCubeModel() { // Create loaders const mtlLoader = new THREE.MTLLoader();