From 5141751d0b68247c24cb3c6195664e9d6f61226a Mon Sep 17 00:00:00 2001 From: Eskimue Date: Wed, 22 Apr 2026 11:37:49 +0200 Subject: [PATCH] Mondjahr verbessert --- public/moonyear.php | 182 ++++++++++++++++++++++++++++---------------- 1 file changed, 118 insertions(+), 64 deletions(-) diff --git a/public/moonyear.php b/public/moonyear.php index 55c8d2e..c0f8be7 100644 --- a/public/moonyear.php +++ b/public/moonyear.php @@ -116,6 +116,7 @@ for ($m = 1; $m <= 12; $m++) { Aktuelles Jahr +
@@ -124,8 +125,8 @@ for ($m = 1; $m <= 12; $m++) {

Zeichne Mondphasen-Jahresübersicht…

- Neumond  ·  Erstes Viertel  ·  Vollmond  ·  Letztes Viertel - — Zeitzone: + NM = Neumond  ·  EV = Erstes Viertel  ·  VM = Vollmond  ·  LV = Letztes Viertel +  ·  Zeitzone:
@@ -184,6 +185,8 @@ for ($m = 1; $m <= 12; $m++) { eventMap[key] = { short: meta.short, color: meta.color, time: e.time }; } + const downloadEl = document.getElementById('moonyearDownload'); + const phaseImage = new Image(); phaseImage.src = ; @@ -249,120 +252,171 @@ for ($m = 1; $m <= 12; $m++) { function draw() { if (!phaseImage.complete || !phaseImage.naturalWidth) return; - const dpr = Math.max(1, window.devicePixelRatio || 1); - - // Layout (CSS pixels) - const labelW = 92; - const dayW = 30; - const rowH = 46; - const headerH = 26; - const padTop = 10; - const padBot = 20; - const padRight = 10; - const radius = 11; - - const cssW = labelW + 31 * dayW + padRight; - const cssH = padTop + headerH + 12 * rowH + padBot; + // DIN A3 Querformat bei 150 DPI: 420mm × 297mm → 2480 × 1754 px + const W = 2480; + const H = 1754; const canvas = document.createElement('canvas'); - canvas.width = Math.round(cssW * dpr); - canvas.height = Math.round(cssH * dpr); + canvas.width = W; + canvas.height = H; const ctx = canvas.getContext('2d'); - ctx.setTransform(dpr, 0, 0, dpr, 0, 0); - // Background - const bg = ctx.createLinearGradient(0, 0, 0, cssH); + // --- Hintergrund --- + const bg = ctx.createLinearGradient(0, 0, 0, H); bg.addColorStop(0, '#02050a'); bg.addColorStop(1, '#04080f'); ctx.fillStyle = bg; - ctx.fillRect(0, 0, cssW, cssH); + ctx.fillRect(0, 0, W, H); - // Day-number header - ctx.fillStyle = 'rgba(201, 168, 76, 0.65)'; - ctx.font = '500 9px "Share Tech Mono", monospace'; + // --- Layout --- + const padH = 48; // links/rechts + const padTop = 48; + const titleH = 72; // Titelzeile oben + const headerH = 44; // Zeile mit Tageszahlen + const footerH = 52; // untere Beschriftungszeile + const labelW = 180; // Spalte für Monatsnamen + + const gridLeft = padH + labelW; + const gridRight = W - padH; + const gridW = gridRight - gridLeft; + const dayW = gridW / 31; + + const gridTop = padTop + titleH + headerH; + const gridBot = H - footerH - 16; + const gridH = gridBot - gridTop; + const rowH = gridH / 12; + + const discRadius = Math.min(dayW * 0.38, rowH * 0.33); + + // --- Titel --- + ctx.fillStyle = 'rgba(240, 215, 132, 0.95)'; + ctx.font = '700 52px Rajdhani, sans-serif'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + ctx.fillText('Mondphasen ' + YEAR, padH + labelW, padTop + titleH / 2); + + // Zeitzone rechts im Titel + ctx.fillStyle = 'rgba(201, 168, 76, 0.55)'; + ctx.font = '400 22px "Share Tech Mono", monospace'; + ctx.textAlign = 'right'; + ctx.fillText('Zeitzone: ', W - padH, padTop + titleH / 2); + + // --- Tageszahlen-Header --- + const headerY = padTop + titleH + headerH / 2; + ctx.fillStyle = 'rgba(201, 168, 76, 0.70)'; + ctx.font = '500 20px "Share Tech Mono", monospace'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; - const headerY = padTop + headerH / 2; for (let d = 1; d <= 31; d++) { - ctx.fillText(String(d), labelW + (d - 1) * dayW + dayW / 2, headerY); + ctx.fillText(String(d), gridLeft + (d - 0.5) * dayW, headerY); } - // Month rows + // Trennlinie unter Header + ctx.strokeStyle = 'rgba(201,168,76,0.25)'; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(padH, padTop + titleH + headerH); + ctx.lineTo(W - padH, padTop + titleH + headerH); + ctx.stroke(); + + // --- Monatszeilen --- for (let m = 1; m <= 12; m++) { - const rowTop = padTop + headerH + (m - 1) * rowH; - const centerY = rowTop + rowH / 2; + const rowTop = gridTop + (m - 1) * rowH; + const centerY = rowTop + rowH * 0.44; const days = daysInMonth(YEAR, m); - // Alternating row tint + // Alternierende Zeile if (m % 2 === 0) { ctx.fillStyle = 'rgba(255,255,255,0.018)'; - ctx.fillRect(0, rowTop, cssW, rowH); + ctx.fillRect(padH, rowTop, W - 2 * padH, rowH); } - // Month name - ctx.fillStyle = 'rgba(240, 217, 132, 0.85)'; - ctx.font = '500 11px Rajdhani, sans-serif'; + // Monatsname + ctx.fillStyle = 'rgba(240, 217, 132, 0.90)'; + ctx.font = '600 28px Rajdhani, sans-serif'; ctx.textAlign = 'right'; ctx.textBaseline = 'middle'; - ctx.fillText(MONTH_NAMES[m - 1], labelW - 8, centerY - 5); + ctx.fillText(MONTH_NAMES[m - 1], padH + labelW - 14, centerY); - // Moon discs & phase labels + // Mondscheiben & Phasenbeschriftungen for (let d = 1; d <= 31; d++) { - const x = labelW + (d - 1) * dayW + dayW / 2; + const x = gridLeft + (d - 0.5) * dayW; if (d > days) { - ctx.fillStyle = 'rgba(255,255,255,0.05)'; - ctx.fillRect(x - 7, centerY - 0.5, 14, 1); + ctx.fillStyle = 'rgba(255,255,255,0.04)'; + ctx.fillRect(x - 12, centerY - 0.5, 24, 1); continue; } const info = phaseInfoForDay(YEAR, m, d); - drawMoonDisc(ctx, x, centerY - 5, radius, info); + drawMoonDisc(ctx, x, centerY, discRadius, info); - // Phase event label from Python data const key = m + '-' + d; if (eventMap[key]) { - const ev = eventMap[key]; + const ev = eventMap[key]; + const labelY = centerY + discRadius + 6; ctx.fillStyle = ev.color; - ctx.font = '600 7.5px "Share Tech Mono", monospace'; + ctx.font = '700 15px "Share Tech Mono", monospace'; ctx.textAlign = 'center'; ctx.textBaseline = 'top'; - ctx.fillText(ev.short, x, centerY - 5 + radius + 2); - // Time below label + ctx.fillText(ev.short, x, labelY); if (ev.time) { - ctx.fillStyle = 'rgba(180,180,180,0.55)'; - ctx.font = '400 6.5px "Share Tech Mono", monospace'; - ctx.fillText(ev.time, x, centerY - 5 + radius + 11); + ctx.fillStyle = 'rgba(180,180,180,0.60)'; + ctx.font = '400 13px "Share Tech Mono", monospace'; + ctx.fillText(ev.time, x, labelY + 17); } } } - // Row separator + // Zeilentrenner ctx.strokeStyle = 'rgba(201,168,76,0.07)'; ctx.lineWidth = 1; ctx.beginPath(); - ctx.moveTo(0, rowTop + rowH); - ctx.lineTo(cssW, rowTop + rowH); + ctx.moveTo(padH, rowTop + rowH); + ctx.lineTo(W - padH, rowTop + rowH); ctx.stroke(); } - // Vertical separator after label column - ctx.strokeStyle = 'rgba(201,168,76,0.15)'; + // Vertikaler Trenner nach Monatsnamen-Spalte + ctx.strokeStyle = 'rgba(201,168,76,0.18)'; ctx.lineWidth = 1; ctx.beginPath(); - ctx.moveTo(labelW, padTop); - ctx.lineTo(labelW, cssH - padBot); + ctx.moveTo(padH + labelW, padTop + titleH); + ctx.lineTo(padH + labelW, gridBot); ctx.stroke(); - // Footer - ctx.fillStyle = 'rgba(240, 215, 132, 0.40)'; - ctx.font = '400 8px "Share Tech Mono", monospace'; - ctx.textAlign = 'right'; - ctx.textBaseline = 'bottom'; - ctx.fillText('NM=Neumond EV=Erstes Viertel VM=Vollmond LV=Letztes Viertel', cssW - padRight, cssH - 4); + // --- Fußzeile --- + const fy = H - footerH / 2 - 8; - imageEl.src = canvas.toDataURL('image/png'); - statusEl.textContent = 'Mondphasen ' + YEAR + ' — Zeitzone: '; + // Links: Legende + ctx.fillStyle = 'rgba(201, 168, 76, 0.55)'; + ctx.font = '400 18px "Share Tech Mono", monospace'; + ctx.textAlign = 'left'; + ctx.textBaseline = 'middle'; + ctx.fillText('NM = Neumond · EV = Erstes Viertel · VM = Vollmond · LV = Letztes Viertel', padH, fy); + + // Rechts: Museumssignatur + ctx.fillStyle = 'rgba(240, 215, 132, 0.70)'; + ctx.font = '500 20px Rajdhani, sans-serif'; + ctx.textAlign = 'right'; + ctx.fillText('Astronomiemuseum der Sternwarte Sonneberg', W - padH, fy); + + // Trennlinie über Fußzeile + ctx.strokeStyle = 'rgba(201,168,76,0.20)'; + ctx.lineWidth = 1; + ctx.beginPath(); + ctx.moveTo(padH, H - footerH - 16); + ctx.lineTo(W - padH, H - footerH - 16); + ctx.stroke(); + + // --- Bild ausgeben --- + const dataUrl = canvas.toDataURL('image/png'); + imageEl.src = dataUrl; + if (downloadEl) { + downloadEl.href = dataUrl; + downloadEl.style.display = ''; + } + statusEl.textContent = 'Mondphasen ' + YEAR + ' (DIN A3, 150 DPI) — Zeitzone: '; } phaseImage.onload = draw;