Freie Kamera eingebaut in Mondphase

This commit is contained in:
2026-04-07 09:03:33 +02:00
parent 7653d6e62e
commit 071eab9a1c
2 changed files with 69 additions and 8 deletions
+8 -2
View File
@@ -2230,8 +2230,8 @@ body.moonphase-page.has-modal {
grid-template-columns: 1fr;
grid-template-rows: minmax(2.8em, auto) auto;
justify-content: flex-start;
min-height: 124px;
height: 124px;
min-height: 165px;
height: 190px;
align-content: start;
}
@@ -2247,6 +2247,12 @@ body.moonphase-page.has-modal {
color: #d8f7ff;
}
.moon-orbit-btn.is-active {
background: rgba(169, 255, 143, 0.15);
border-color: #6fc85a;
color: #c8ffc0;
}
.moon-phase-name {
font-family: 'Orbitron', sans-serif;
font-size: 1.15rem;
+61 -6
View File
@@ -162,6 +162,7 @@ try {
<div class="moon-phase-name" id="phaseName"></div>
<div class="moon-actions">
<button type="button" class="date-shift-btn moon-reset-btn" id="resetViewBtn" title="Grafik reset">Grafik reset</button>
<button type="button" class="date-shift-btn moon-orbit-btn" id="freeCameraBtn" title="Kamera frei um den Mond bewegen">Freie Kamera</button>
<button
type="button"
class="date-shift-btn moon-action-btn"
@@ -334,6 +335,7 @@ const prevDayBtn = document.getElementById('prevDayBtn');
const nextDayBtn = document.getElementById('nextDayBtn');
const nowBtn = document.getElementById('nowBtn');
const resetViewBtn = document.getElementById('resetViewBtn');
const freeCameraBtn = document.getElementById('freeCameraBtn');
const playPauseBtn = document.getElementById('playPauseBtn');
const playbackSpeedGroup = document.getElementById('playbackSpeedGroup');
const exportMoonBtn = document.getElementById('exportMoonBtn');
@@ -363,6 +365,11 @@ let moonPlaybackActive = false;
let moonPlaybackSpeed = 1;
let isMoonExportRunning = false;
let moonExportRestoreFocus = null;
let freeCameraMode = false;
let cameraOrbitTheta = 0;
let cameraOrbitPhi = Math.PI / 2;
let moonDragStartOrbitTheta = 0;
let moonDragStartOrbitPhi = Math.PI / 2;
const craterLongitudeOffsetDeg = 0.0;
const craterLatitudeOffsetDeg = 0.0;
const craterOverlayScale = 0.940;
@@ -492,9 +499,20 @@ function applyMoonZoom() {
return;
}
moonSceneState.camera.position.z = MOON_CAMERA_BASE_Z / getMoonZoomFactor();
moonSceneState.camera.position.x = moonPanX;
moonSceneState.camera.position.y = moonPanY;
const r = MOON_CAMERA_BASE_Z / getMoonZoomFactor();
if (freeCameraMode) {
moonSceneState.camera.position.set(
r * Math.sin(cameraOrbitPhi) * Math.sin(cameraOrbitTheta),
r * Math.cos(cameraOrbitPhi),
r * Math.sin(cameraOrbitPhi) * Math.cos(cameraOrbitTheta)
);
moonSceneState.camera.lookAt(0, 0, 0);
} else {
moonSceneState.camera.position.set(moonPanX, moonPanY, r);
moonSceneState.camera.lookAt(moonPanX, moonPanY, 0);
}
moonSceneState.camera.updateProjectionMatrix();
}
@@ -506,6 +524,8 @@ function resetMoonView() {
moonZoom = 1;
moonPanX = 0;
moonPanY = 0;
cameraOrbitTheta = 0;
cameraOrbitPhi = Math.PI / 2;
updateMoon();
}
@@ -1130,6 +1150,17 @@ function getMoonDisplayGeometry(width, height, zoom = null) {
if (moonSceneState && moonSceneState.camera) {
const camera = moonSceneState.camera;
const focalLengthPx = (height / 2) / Math.tan(toRadians(camera.fov / 2));
if (freeCameraMode) {
const depth = Math.max(0.001, camera.position.length());
const projectedRadius = focalLengthPx * (1.6 / depth) * MOON_PROJECTED_RADIUS_SCALE;
return {
centerX: width / 2,
centerY: height / 2,
radius: projectedRadius
};
}
const depth = Math.max(0.001, camera.position.z);
const projectedRadius = focalLengthPx * (1.6 / depth) * MOON_PROJECTED_RADIUS_SCALE;
@@ -1463,6 +1494,8 @@ function beginMoonDrag(event) {
moonDragStartY = event.clientY;
moonDragStartPanX = moonPanX;
moonDragStartPanY = moonPanY;
moonDragStartOrbitTheta = cameraOrbitTheta;
moonDragStartOrbitPhi = cameraOrbitPhi;
moonCanvas.classList.add('is-dragging');
moonCanvas.setPointerCapture(event.pointerId);
}
@@ -1474,10 +1507,21 @@ function updateMoonDrag(event) {
const rect = moonCanvas.getBoundingClientRect();
const size = Math.max(1, Math.min(rect.width, rect.height));
const panScale = 4.2 / (size * getMoonZoomFactor());
moonPanX = clampMoonPan(moonDragStartPanX - (event.clientX - moonDragStartX) * panScale);
moonPanY = clampMoonPan(moonDragStartPanY + (event.clientY - moonDragStartY) * panScale);
if (freeCameraMode) {
const sensitivity = 3.0 / size;
cameraOrbitTheta = moonDragStartOrbitTheta + (event.clientX - moonDragStartX) * sensitivity;
cameraOrbitPhi = THREE.MathUtils.clamp(
moonDragStartOrbitPhi - (event.clientY - moonDragStartY) * sensitivity,
0.05,
Math.PI - 0.05
);
} else {
const panScale = 4.2 / (size * getMoonZoomFactor());
moonPanX = clampMoonPan(moonDragStartPanX - (event.clientX - moonDragStartX) * panScale);
moonPanY = clampMoonPan(moonDragStartPanY + (event.clientY - moonDragStartY) * panScale);
}
updateMoon();
}
@@ -1527,6 +1571,17 @@ nowBtn.addEventListener('click', () => {
updateMoon();
});
resetViewBtn.addEventListener('click', resetMoonView);
freeCameraBtn.addEventListener('click', () => {
freeCameraMode = !freeCameraMode;
freeCameraBtn.classList.toggle('is-active', freeCameraMode);
if (!freeCameraMode) {
moonPanX = 0;
moonPanY = 0;
}
cameraOrbitTheta = 0;
cameraOrbitPhi = Math.PI / 2;
updateMoon();
});
playPauseBtn.addEventListener('click', toggleMoonPlayback);
exportMoonBtn.addEventListener('click', openMoonExportDialog);
showAllCratersBtn.addEventListener('click', () => {