Verbessere Mondfinsternis-Steuerung und Zeitanzeige

This commit is contained in:
2026-08-19 10:38:35 +02:00
parent 2bf2848905
commit 2bf6c504b5
2 changed files with 190 additions and 22 deletions
+55
View File
@@ -4848,6 +4848,13 @@ body.solarsystem-page:not(.jupitersystem-page) .solarsystem-stage-wrap {
line-height: 1.4; line-height: 1.4;
} }
.eclipse-info-scale-note {
margin-top: 0.8rem;
font-size: 0.74rem;
line-height: 1.45;
color: var(--text-dim);
}
/* Info panel: ein-/ausklappbar */ /* Info panel: ein-/ausklappbar */
.eclipse-info-panel { .eclipse-info-panel {
padding-top: 2.4rem; padding-top: 2.4rem;
@@ -4892,6 +4899,37 @@ body.solarsystem-page:not(.jupitersystem-page) .solarsystem-stage-wrap {
padding: 0.55rem 0.6rem; padding: 0.55rem 0.6rem;
} }
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.eclipse-stage-time {
position: absolute;
left: 50%;
bottom: calc(5.4rem + 15px);
transform: translateX(-50%);
z-index: 9;
padding: 0.45rem 0.8rem;
border-radius: 999px;
border: 1px solid rgba(201, 168, 76, 0.18);
background: rgba(5, 9, 18, 0.82);
backdrop-filter: blur(8px);
color: var(--gold-pale);
font-size: 0.88rem;
font-variant-numeric: tabular-nums;
letter-spacing: 0.04em;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28);
pointer-events: none;
}
/* View toggle */ /* View toggle */
.eclipse-view-toggle { .eclipse-view-toggle {
position: absolute; position: absolute;
@@ -5003,6 +5041,23 @@ body.solarsystem-page:not(.jupitersystem-page) .solarsystem-stage-wrap {
.eclipse-tl-max .eclipse-tl-label { color: var(--gold); } .eclipse-tl-max .eclipse-tl-label { color: var(--gold); }
.eclipse-tl-tot .eclipse-tl-label { color: #f05530; } .eclipse-tl-tot .eclipse-tl-label { color: #f05530; }
.eclipse-sidebar-controls {
display: grid;
gap: 0.7rem;
}
.eclipse-sidebar-buttons {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
}
.eclipse-speed-label-sidebar {
display: grid;
gap: 0.3rem;
justify-items: start;
}
.eclipse-timeline-controls { .eclipse-timeline-controls {
display: flex; display: flex;
align-items: center; align-items: center;
+135 -22
View File
@@ -42,8 +42,42 @@ $userLon = isset($currentLocation['longitude']) ? (float)$currentLocation['longi
</div> </div>
</div> </div>
<div class="solarsystem-sidebar-block">
<div class="solarsystem-label">Steuerung</div>
<div class="eclipse-sidebar-controls">
<div class="eclipse-sidebar-buttons">
<button class="btn btn-sm btn-secondary" id="eclipseBtnRewind" title="Zum Anfang">⏮</button>
<button class="btn btn-sm btn-primary" id="eclipseBtnPlay" title="Play/Pause">▶ Play</button>
<button class="btn btn-sm btn-secondary" id="eclipseBtnEnd" title="Zum Ende">⏭</button>
</div>
<div class="eclipse-time-display" id="eclipseTimeDisplay">– – : – – UTC</div>
<label class="eclipse-speed-label eclipse-speed-label-sidebar">
<span class="solarsystem-label">Geschw.</span>
<select id="eclipseSpeedSel" class="eclipse-speed-sel">
<option value="1">×1</option>
<option value="3">×3</option>
<option value="10" selected>×10</option>
<option value="30">×30</option>
<option value="60">×60</option>
</select>
</label>
</div>
</div>
<div class="solarsystem-sidebar-block">
<div class="solarsystem-label">Zeitformat</div>
<label class="form-checkbox" for="eclipseUseLocalTime">
<input type="checkbox" id="eclipseUseLocalTime">
<span>Lokale Zeit statt UTC anzeigen</span>
</label>
<label class="form-checkbox" for="eclipseShowStageTime">
<input type="checkbox" id="eclipseShowStageTime">
<span>Uhrzeit mittig in der Grafik anzeigen</span>
</label>
</div>
<div class="solarsystem-sidebar-block is-hidden" id="eclipseDataBlock"> <div class="solarsystem-sidebar-block is-hidden" id="eclipseDataBlock">
<div class="solarsystem-label">Kontaktzeiten (UTC)</div> <div class="solarsystem-label">Kontaktzeiten (<span id="eclipseTimeZoneLabel">UTC</span>)</div>
<table class="data-table" id="eclipseContactTable"></table> <table class="data-table" id="eclipseContactTable"></table>
</div> </div>
@@ -64,30 +98,40 @@ $userLon = isset($currentLocation['longitude']) ? (float)$currentLocation['longi
<!-- 2D Moon canvas --> <!-- 2D Moon canvas -->
<canvas id="eclipseMoonCanvas" class="eclipse-canvas is-hidden"></canvas> <canvas id="eclipseMoonCanvas" class="eclipse-canvas is-hidden"></canvas>
<!-- Scale notice -->
<div id="eclipseScaleNote" class="eclipse-scale-note">
Abstände nicht maßstabsgetreu<br>(Mond real ~60× weiter entfernt)
</div>
<!-- Info overlay --> <!-- Info overlay -->
<div class="solarsystem-info-panel" id="eclipseInfoPanel"> <div class="solarsystem-info-panel eclipse-info-panel" id="eclipseInfoPanel">
<div class="eclipse-info-empty">Wähle eine Finsternis aus der Liste</div> <button
type="button"
class="eclipse-info-toggle"
id="eclipseInfoToggle"
aria-expanded="true"
aria-controls="eclipseInfoBody"
title="Infokasten ein- oder ausklappen"
>
<span class="eclipse-info-toggle-icon" aria-hidden="true">▾</span>
<span class="sr-only">Infokasten ein- oder ausklappen</span>
</button>
<div class="eclipse-info-body" id="eclipseInfoBody">
<div class="eclipse-info-empty">Wähle eine Finsternis aus der Liste</div>
<div class="eclipse-info-scale-note">Abstände nicht maßstabsgetreu, Mond real etwa 60× weiter entfernt</div>
</div>
</div> </div>
<div class="eclipse-stage-time is-hidden" id="eclipseStageTime">– – : – – UTC</div>
<!-- Timeline --> <!-- Timeline -->
<div class="eclipse-timeline" id="eclipseTimeline"> <div class="eclipse-timeline" id="eclipseTimeline">
<div class="eclipse-timeline-bar" id="eclipseTimelineBar"> <div class="eclipse-timeline-bar" id="eclipseTimelineBar">
<div class="eclipse-tl-cursor" id="eclipseTimelineCursor"></div> <div class="eclipse-tl-cursor" id="eclipseTimelineCursor"></div>
</div> </div>
<div class="eclipse-timeline-controls"> <div class="eclipse-timeline-controls is-hidden" aria-hidden="true">
<button class="btn btn-sm btn-secondary" id="eclipseBtnRewind" title="Zum Anfang">⏮</button> <button class="btn btn-sm btn-secondary" id="eclipseBtnRewindLegacy" title="Zum Anfang">⏮</button>
<button class="btn btn-sm btn-primary" id="eclipseBtnPlay" title="Play/Pause">▶ Play</button> <button class="btn btn-sm btn-primary" id="eclipseBtnPlayLegacy" title="Play/Pause">▶ Play</button>
<button class="btn btn-sm btn-secondary" id="eclipseBtnEnd" title="Zum Ende">⏭</button> <button class="btn btn-sm btn-secondary" id="eclipseBtnEndLegacy" title="Zum Ende">⏭</button>
<span class="eclipse-time-display" id="eclipseTimeDisplay">– – : – – UTC</span> <span class="eclipse-time-display" id="eclipseTimeDisplayLegacy">– – : – – UTC</span>
<label class="eclipse-speed-label"> <label class="eclipse-speed-label">
<span class="solarsystem-label">Geschw.</span> <span class="solarsystem-label">Geschw.</span>
<select id="eclipseSpeedSel" class="eclipse-speed-sel"> <select id="eclipseSpeedSelLegacy" class="eclipse-speed-sel">
<option value="1">×1</option> <option value="1">×1</option>
<option value="3">×3</option> <option value="3">×3</option>
<option value="10" selected>×10</option> <option value="10" selected>×10</option>
@@ -140,9 +184,14 @@ const canvas3d = document.getElementById('eclipseCanvas');
const canvas2d = document.getElementById('eclipseMoonCanvas'); const canvas2d = document.getElementById('eclipseMoonCanvas');
const stageWrap = document.getElementById('eclipseStageWrap'); const stageWrap = document.getElementById('eclipseStageWrap');
const infoPanel = document.getElementById('eclipseInfoPanel'); const infoPanel = document.getElementById('eclipseInfoPanel');
const infoToggle = document.getElementById('eclipseInfoToggle');
const timelineBar = document.getElementById('eclipseTimelineBar'); const timelineBar = document.getElementById('eclipseTimelineBar');
const tlCursor = document.getElementById('eclipseTimelineCursor'); const tlCursor = document.getElementById('eclipseTimelineCursor');
const tlDisplay = document.getElementById('eclipseTimeDisplay'); const tlDisplay = document.getElementById('eclipseTimeDisplay');
const useLocalTimeEl = document.getElementById('eclipseUseLocalTime');
const showStageTimeEl = document.getElementById('eclipseShowStageTime');
const timeZoneLabelEl = document.getElementById('eclipseTimeZoneLabel');
const stageTimeEl = document.getElementById('eclipseStageTime');
const eclipseListEl = document.getElementById('eclipseList'); const eclipseListEl = document.getElementById('eclipseList');
const dataBlock = document.getElementById('eclipseDataBlock'); const dataBlock = document.getElementById('eclipseDataBlock');
const contactTable = document.getElementById('eclipseContactTable'); const contactTable = document.getElementById('eclipseContactTable');
@@ -161,6 +210,9 @@ let playing = false;
let playSpeed = 10; // minutes / real second let playSpeed = 10; // minutes / real second
let lastRAF = 0; let lastRAF = 0;
let viewMode = 'geo'; let viewMode = 'geo';
let infoPanelCollapsed = false;
let useLocalTime = false;
let showStageTime = false;
// Eclipse geometry (computed per eclipse) // Eclipse geometry (computed per eclipse)
let eclB = 0; // impact parameter (vis-units) let eclB = 0; // impact parameter (vis-units)
@@ -603,7 +655,7 @@ function updateSim() {
// ── Time display ──────────────────────────────── // ── Time display ────────────────────────────────
const peakMs = currentEclipse.peak.date.getTime(); const peakMs = currentEclipse.peak.date.getTime();
const cur = new Date(peakMs + simTime * 60000); const cur = new Date(peakMs + simTime * 60000);
tlDisplay.textContent = cur.toISOString().slice(11, 16) + ' UTC'; tlDisplay.textContent = formatTime(cur, { withSeconds: false });
// ── 2D moon view ──────────────────────────────── // ── 2D moon view ────────────────────────────────
if (viewMode === 'moon') drawMoonView(mp, sdw); if (viewMode === 'moon') drawMoonView(mp, sdw);
@@ -775,15 +827,49 @@ function phaseName(t) {
return t < 0 ? 'VOR DER FINSTERNIS' : 'NACH DER FINSTERNIS'; return t < 0 ? 'VOR DER FINSTERNIS' : 'NACH DER FINSTERNIS';
} }
function fmtUTC(peakDate, offsetMin) { function timeZoneSuffix() {
return new Date(peakDate.getTime() + offsetMin * 60000).toISOString().slice(11, 19) + ' UTC'; return useLocalTime ? 'Uhr' : 'UTC';
}
function formatDate(date, options) {
const settings = useLocalTime ? {} : { timeZone: 'UTC' };
return date.toLocaleDateString('de-DE', { ...options, ...settings });
}
function formatTime(date, { withSeconds = true } = {}) {
const settings = {
hour: '2-digit',
minute: '2-digit',
...(withSeconds ? { second: '2-digit' } : {}),
hour12: false,
...(useLocalTime ? {} : { timeZone: 'UTC' }),
};
return `${date.toLocaleTimeString('de-DE', settings)} ${timeZoneSuffix()}`;
}
function refreshTimeDisplays() {
timeZoneLabelEl.textContent = timeZoneSuffix();
if (!currentEclipse) {
tlDisplay.textContent = `– – : – – ${timeZoneSuffix()}`;
return;
}
renderInfo();
renderContactTable();
updateSim();
}
function setInfoPanelCollapsed(collapsed) {
infoPanelCollapsed = collapsed;
infoPanel.classList.toggle('is-collapsed', collapsed);
infoToggle.setAttribute('aria-expanded', String(!collapsed));
infoToggle.title = collapsed ? 'Infokasten aufklappen' : 'Infokasten zuklappen';
} }
function renderInfo() { function renderInfo() {
const ec = currentEclipse; const ec = currentEclipse;
const d = ec.peak.date; const d = ec.peak.date;
const ds = d.toLocaleDateString('de-DE', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }); const ds = formatDate(d, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
const ts = d.toISOString().slice(11, 19) + ' UTC'; const ts = formatTime(d);
const tc = { penumbral: 'eclipse-type-pen', partial: 'eclipse-type-par', total: 'eclipse-type-tot' }[ec.kind] || ''; const tc = { penumbral: 'eclipse-type-pen', partial: 'eclipse-type-par', total: 'eclipse-type-tot' }[ec.kind] || '';
const tl = { penumbral: 'Halbschatten-Finsternis', partial: 'Partielle Mondfinsternis', total: 'Totale Mondfinsternis' }[ec.kind] || ec.kind; const tl = { penumbral: 'Halbschatten-Finsternis', partial: 'Partielle Mondfinsternis', total: 'Totale Mondfinsternis' }[ec.kind] || ec.kind;
@@ -791,7 +877,7 @@ function renderInfo() {
? ec.umbral_magnitude.toFixed(3) ? ec.umbral_magnitude.toFixed(3)
: (ec.penumbral_magnitude !== undefined ? ec.penumbral_magnitude.toFixed(3) + ' (Hb)' : '—'); : (ec.penumbral_magnitude !== undefined ? ec.penumbral_magnitude.toFixed(3) + ' (Hb)' : '—');
infoPanel.innerHTML = ` document.getElementById('eclipseInfoBody').innerHTML = `
<div class="eclipse-info-type ${tc}">${tl}</div> <div class="eclipse-info-type ${tc}">${tl}</div>
<div class="eclipse-info-date">${ds}</div> <div class="eclipse-info-date">${ds}</div>
<div class="eclipse-info-time">${ts}</div> <div class="eclipse-info-time">${ts}</div>
@@ -802,6 +888,7 @@ function renderInfo() {
<tr><td>Magnitude</td><td>${mag}</td></tr> <tr><td>Magnitude</td><td>${mag}</td></tr>
</table> </table>
<div class="eclipse-info-phase" id="eclipsePhaseLabel">—</div> <div class="eclipse-info-phase" id="eclipsePhaseLabel">—</div>
<div class="eclipse-info-scale-note">Abstände nicht maßstabsgetreu, Mond real etwa 60× weiter entfernt</div>
`; `;
} }
@@ -832,7 +919,7 @@ function renderContactTable() {
contactTable.innerHTML = rows.map(([lbl, off, desc]) => contactTable.innerHTML = rows.map(([lbl, off, desc]) =>
`<tr> `<tr>
<td class="eclipse-contact-label">${lbl}</td> <td class="eclipse-contact-label">${lbl}</td>
<td class="eclipse-contact-time">${fmtUTC(pk, off)}</td> <td class="eclipse-contact-time">${formatTime(new Date(pk.getTime() + off * 60000))}</td>
<td class="eclipse-contact-desc">${desc}</td> <td class="eclipse-contact-desc">${desc}</td>
</tr>` </tr>`
).join(''); ).join('');
@@ -886,9 +973,25 @@ function renderTimeline() {
} }
} }
infoToggle.addEventListener('click', () => {
setInfoPanelCollapsed(!infoPanelCollapsed);
});
// ════════════════════════════════════════════════════════════ // ════════════════════════════════════════════════════════════
// Interaction // Interaction
// ════════════════════════════════════════════════════════════ // ════════════════════════════════════════════════════════════
const originalUpdateSim = updateSim;
updateSim = function() {
originalUpdateSim();
stageTimeEl.textContent = tlDisplay.textContent;
};
const originalRefreshTimeDisplays = refreshTimeDisplays;
refreshTimeDisplays = function() {
originalRefreshTimeDisplays();
stageTimeEl.textContent = tlDisplay.textContent;
};
function setupInteraction() { function setupInteraction() {
// Camera orbit // Camera orbit
canvas3d.addEventListener('mousedown', e => { isDragging = true; dragX = e.clientX; dragY = e.clientY; }); canvas3d.addEventListener('mousedown', e => { isDragging = true; dragX = e.clientX; dragY = e.clientY; });
@@ -949,6 +1052,16 @@ function setupInteraction() {
// Speed // Speed
speedSel.addEventListener('change', () => { playSpeed = parseInt(speedSel.value); }); speedSel.addEventListener('change', () => { playSpeed = parseInt(speedSel.value); });
useLocalTimeEl.addEventListener('change', () => {
useLocalTime = useLocalTimeEl.checked;
refreshTimeDisplays();
});
showStageTimeEl.addEventListener('change', () => {
showStageTime = showStageTimeEl.checked;
stageTimeEl.classList.toggle('is-hidden', !showStageTime);
});
// View toggle // View toggle
btnGeoView.addEventListener('click', () => setView('geo')); btnGeoView.addEventListener('click', () => setView('geo'));
btnMoonView.addEventListener('click', () => setView('moon')); btnMoonView.addEventListener('click', () => setView('moon'));