diff --git a/app.js b/app.js index 5a028f6..c61a3b4 100644 --- a/app.js +++ b/app.js @@ -144,6 +144,58 @@ document.addEventListener('keydown', (e) => { } }); +// === PHOTO MODE (P key — hide UI, photography controls, depth of field) === +let photoMode = false; +const photoIndicator = document.getElementById('photo-indicator'); +const uiElements = document.querySelectorAll('.hud-controls'); + +// Toggle photo mode with 'P' key +document.addEventListener('keydown', (e) => { + if (e.key === 'p' || e.key === 'P') { + e.preventDefault(); + photoMode = !photoMode; + if (photoMode) { + photoIndicator.classList.add('visible'); + uiElements.forEach(el => el.style.display = 'none'); + camera.near = 0.5; + camera.far = 500; + camera.updateProjectionMatrix(); + } else { + photoIndicator.classList.remove('visible'); + uiElements.forEach(el => el.style.display = ''); + camera.near = 0.1; + camera.far = 2000; + camera.updateProjectionMatrix(); + } + } +}); + +// Enhanced camera control in photo mode +let photoCamSpeed = 0.1; +let photoCamPitch = 0; +let photoCamYaw = 0; + +document.addEventListener('mousemove', (e) => { + mouseX = (e.clientX / window.innerWidth - 0.5) * 2; + mouseY = (e.clientY / window.innerHeight - 0.5) * 2; + if (photoMode) { + photoCamYaw = -mouseX * Math.PI; + photoCamPitch = mouseY * Math.PI * 0.5; + } +}); + +// Depth of field approximation via focal plane +document.addEventListener('keydown', (e) => { + if (!photoMode) return; + if (e.key === 'ArrowUp') { + camera.focusDistance = Math.max(1, (camera.focusDistance || 10) - 1); + e.preventDefault(); + } else if (e.key === 'ArrowDown') { + camera.focusDistance = (camera.focusDistance || 10) + 1; + e.preventDefault(); + } +}); + // === RESIZE HANDLER === window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; @@ -166,7 +218,15 @@ function animate() { const targetT = overviewMode ? 1 : 0; overviewT += (targetT - overviewT) * 0.04; camera.position.lerpVectors(NORMAL_CAM, OVERVIEW_CAM, overviewT); - camera.lookAt(0, 0, 0); + if (photoMode) { + const forward = new THREE.Vector3(0, 0, -1); + forward.applyAxisAngle(new THREE.Vector3(0, 1, 0), photoCamYaw); + forward.applyAxisAngle(new THREE.Vector3(1, 0, 0), photoCamPitch); + const target = camera.position.clone().add(forward); + camera.lookAt(target); + } else { + camera.lookAt(0, 0, 0); + } // Slow auto-rotation — suppressed during overview so the map stays readable const rotationScale = 1 - overviewT; diff --git a/index.html b/index.html index 26344f3..79ad98a 100644 --- a/index.html +++ b/index.html @@ -40,6 +40,10 @@ MAP VIEW [Tab] to exit +
+ PHOTO MODE + [P] to exit, Arrows adjust focus +
diff --git a/style.css b/style.css index 1d78e52..63782ef 100644 --- a/style.css +++ b/style.css @@ -96,6 +96,41 @@ canvas { display: block; } +#photo-indicator { + display: none; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: var(--color-primary); + font-family: var(--font-body); + font-size: 11px; + letter-spacing: 0.2em; + text-transform: uppercase; + pointer-events: none; + z-index: 20; + border: 1px solid var(--color-primary); + padding: 4px 10px; + background: rgba(0, 0, 8, 0.6); + white-space: nowrap; + animation: photo-pulse 2s ease-in-out infinite; +} + +#photo-indicator.visible { + display: block; +} + +.photo-hint { + margin-left: 12px; + color: var(--color-text-muted); + font-size: 10px; +} + +@keyframes photo-pulse { + 0%, 100% { opacity: 0.7; } + 50% { opacity: 1; } +} + .overview-hint { margin-left: 12px; color: var(--color-text-muted);