fecd8bb9c9
562 Ersetzungen in 50 Dateien: Halbgeviertstrich zwischen zwei Ziffern (= "bis"-Bereich) bekommt Leerzeichen davor/danach (3–4 -> 3 – 4). Regex /([0-9])\s*–\s*(?=[0-9])/ auf Rohtext (format-erhaltend, CRLF/Einrueckung unveraendert), Bindestriche in Woertern und Minuszeichen unangetastet, alle JSON weiter gueltig. Energiemanager-Zeitbloecke sind Display-Labels (kein Logik-Key) -> safe. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
195 lines
8.6 KiB
JavaScript
195 lines
8.6 KiB
JavaScript
/**
|
||
* Glossar-Experimente — Gruppe „monsun".
|
||
*
|
||
* Registrierung via window.GlossarExperiments[key_slug] = fn(root).
|
||
* Eigener svgEl-Helper, nur bestehende .ge-*-CSS-Klassen (aus glossar.php).
|
||
* Keine Libraries. Mobile-/Tap-freundlich. rAF-Loops stoppen bei geschlossenem
|
||
* Modal über den root.isConnected-Guard.
|
||
*/
|
||
|
||
(function () {
|
||
'use strict';
|
||
|
||
const NS = 'http://www.w3.org/2000/svg';
|
||
const svgEl = (tag, attrs, parent) => {
|
||
const el = document.createElementNS(NS, tag);
|
||
if (attrs) for (const k in attrs) el.setAttribute(k, attrs[k]);
|
||
if (parent) parent.appendChild(el);
|
||
return el;
|
||
};
|
||
const deAT = (n) => Number(n).toLocaleString('de-AT');
|
||
|
||
// =====================================================================
|
||
// Experiment: Monsun-Regen — Regen pro Monat in Mumbai (Balkendiagramm)
|
||
// =====================================================================
|
||
function monsunRain(root) {
|
||
root.innerHTML = `
|
||
<div class="ge-wrap">
|
||
<div class="ge-head">
|
||
<h3>🌧 Wann fällt der Regen in Indien?</h3>
|
||
<p>Jeder Balken ist ein Monat in <strong>Mumbai</strong> (Indien). Schau, wie riesig die Balken von <strong>Juni bis September</strong> werden. In dieser kurzen Zeit fällt fast der ganze Jahresregen — davon hängt die Reisernte ab.</p>
|
||
</div>
|
||
<svg class="ge-mon-svg" viewBox="0 0 600 340" preserveAspectRatio="xMidYMid meet" role="img" aria-label="Balkendiagramm Regen pro Monat in Mumbai"></svg>
|
||
<div class="ge-controls">
|
||
<button class="ge-btn ge-btn-primary" data-scen="normal">☔ Normales Jahr</button>
|
||
<button class="ge-btn" data-scen="weak">🌵 Schwacher Monsun</button>
|
||
</div>
|
||
<div class="ge-gh-result">
|
||
<div class="ge-gh-pill" id="ge-mon-share"></div>
|
||
<div class="ge-gh-pill" id="ge-mon-total"></div>
|
||
</div>
|
||
<p class="ge-note" id="ge-mon-note"></p>
|
||
</div>
|
||
`;
|
||
|
||
const svg = root.querySelector('.ge-mon-svg');
|
||
const sharePill = root.querySelector('#ge-mon-share');
|
||
const totalPill = root.querySelector('#ge-mon-total');
|
||
const noteEl = root.querySelector('#ge-mon-note');
|
||
if (!svg || !sharePill || !totalPill || !noteEl) return;
|
||
|
||
const MONTHS = ['Jän', 'Feb', 'Mär', 'Apr', 'Mai', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dez'];
|
||
// Monsun-Monate (0-basiert): Juni–September
|
||
const MONSOON = [5, 6, 7, 8];
|
||
|
||
// Regen pro Monat in mm.
|
||
// Normal: Mumbai (Colaba) 1981 – 2010, India Meteorological Department.
|
||
const NORMAL = [1, 0, 1, 0, 16, 506, 769, 472, 356, 82, 9, 3];
|
||
// Schwacher Monsun (Dürre-Jahr): Spitzen-Monate stark reduziert.
|
||
const WEAK = [1, 0, 1, 0, 10, 280, 420, 240, 180, 60, 6, 2];
|
||
// Vergleich: Wien fällt Monat für Monat ziemlich gleichmäßig (~60 mm).
|
||
const WIEN_REF = 60;
|
||
|
||
const NOTES = {
|
||
normal: 'Regen pro Monat in Mumbai, Normalwerte 1981 – 2010. Quelle: India Meteorological Department (über „Climate of Mumbai"). Ganzes Jahr rund 2.215 mm. Die dünne Linie zeigt Wien: dort fallen jeden Monat etwa 60 mm — ziemlich gleichmäßig.',
|
||
weak: 'Schwacher Monsun (Dürre-Jahr): geschätzte Werte, in denen die Monsun-Monate viel weniger Regen bringen. So etwas macht Bäuerinnen und Bauern Sorge — der Klimawandel lässt den Monsun unberechenbarer werden. Quelle Normalwerte: India Meteorological Department.'
|
||
};
|
||
|
||
// Layout
|
||
const X0 = 58, X1 = 588; // Plot links/rechts
|
||
const BASE = 278, TOP = 34; // Baseline / oberer Rand
|
||
const PLOT_H = BASE - TOP; // Zeichenhöhe
|
||
const MAX_MM = 800; // feste Skala (Balken schrumpfen sichtbar)
|
||
const PX_PER_MM = PLOT_H / MAX_MM;
|
||
const slot = (X1 - X0) / MONTHS.length;
|
||
const BAR_W = slot * 0.66;
|
||
|
||
const yFor = (mm) => BASE - Math.min(MAX_MM, mm) * PX_PER_MM;
|
||
|
||
// --- Statische Achsen / Gitter ---
|
||
// Y-Gitterlinien + Beschriftung
|
||
[0, 200, 400, 600, 800].forEach(mm => {
|
||
const y = yFor(mm);
|
||
svgEl('line', { x1: X0, y1: y, x2: X1, y2: y, stroke: '#dae8ec', 'stroke-width': mm === 0 ? '1.6' : '1' }, svg);
|
||
const t = svgEl('text', { x: X0 - 8, y: y + 4, 'text-anchor': 'end', 'font-size': '11', fill: '#4a4a4a', 'font-weight': '600' }, svg);
|
||
t.textContent = mm;
|
||
});
|
||
svgEl('text', { x: X0 - 8, y: TOP - 14, 'text-anchor': 'end', 'font-size': '11', fill: '#4a4a4a', 'font-weight': '700' }, svg).textContent = 'mm';
|
||
|
||
// Monat-Beschriftung + farbige Monsun-Markierung am Boden
|
||
const barNodes = [];
|
||
const valNodes = [];
|
||
MONTHS.forEach((m, i) => {
|
||
const cx = X0 + slot * i + slot / 2;
|
||
const isMon = MONSOON.includes(i);
|
||
// Balken (Höhe wird animiert gesetzt)
|
||
const bar = svgEl('rect', {
|
||
x: cx - BAR_W / 2, y: BASE, width: BAR_W, height: 0, rx: 3,
|
||
fill: isMon ? '#2f7d8a' : '#a9c7cd'
|
||
}, svg);
|
||
barNodes.push(bar);
|
||
// Wert-Label über dem Balken (nur für die großen Monsun-Balken sichtbar)
|
||
const val = svgEl('text', {
|
||
x: cx, y: BASE, 'text-anchor': 'middle', 'font-size': '10',
|
||
'font-weight': '800', fill: '#1f4e5a', opacity: '0'
|
||
}, svg);
|
||
valNodes.push(val);
|
||
// Monatsname
|
||
const lbl = svgEl('text', {
|
||
x: cx, y: BASE + 18, 'text-anchor': 'middle', 'font-size': '11',
|
||
'font-weight': isMon ? '800' : '600', fill: isMon ? '#1f4e5a' : '#6a6a6a'
|
||
}, svg);
|
||
lbl.textContent = m;
|
||
});
|
||
|
||
// „Regenzeit"-Klammer über Jun–Sep
|
||
const bx0 = X0 + slot * MONSOON[0] + slot * 0.10;
|
||
const bx1 = X0 + slot * (MONSOON[MONSOON.length - 1] + 1) - slot * 0.10;
|
||
svgEl('line', { x1: bx0, y1: TOP - 6, x2: bx1, y2: TOP - 6, stroke: '#e8833a', 'stroke-width': '2', 'stroke-linecap': 'round' }, svg);
|
||
svgEl('text', { x: (bx0 + bx1) / 2, y: TOP - 12, 'text-anchor': 'middle', 'font-size': '11', 'font-weight': '800', fill: '#e8833a' }, svg).textContent = 'Regenzeit';
|
||
|
||
// Wien-Vergleichslinie (dünn, gestrichelt) + Label
|
||
const wienY = yFor(WIEN_REF);
|
||
svgEl('line', { x1: X0, y1: wienY, x2: X1, y2: wienY, stroke: '#c85c4a', 'stroke-width': '2', 'stroke-dasharray': '6 4' }, svg);
|
||
svgEl('text', { x: X1, y: wienY - 6, 'text-anchor': 'end', 'font-size': '10.5', 'font-weight': '800', fill: '#c85c4a' }, svg)
|
||
.textContent = 'Wien: ~60 mm pro Monat';
|
||
|
||
// --- Zustand + Animation ---
|
||
let animId = null;
|
||
let current = NORMAL;
|
||
|
||
function paint(fromData, toData, scen) {
|
||
cancelAnimationFrame(animId);
|
||
const dur = 650;
|
||
const start = performance.now();
|
||
|
||
function tick(now) {
|
||
if (!root.isConnected) { cancelAnimationFrame(animId); return; }
|
||
const t = Math.min(1, (now - start) / dur);
|
||
const e = 1 - Math.pow(1 - t, 3);
|
||
for (let i = 0; i < barNodes.length; i++) {
|
||
const mm = fromData[i] + (toData[i] - fromData[i]) * e;
|
||
const y = yFor(mm);
|
||
barNodes[i].setAttribute('y', y);
|
||
barNodes[i].setAttribute('height', Math.max(0, BASE - y));
|
||
// Wert nur bei den großen Balken (> 100 mm) zeigen
|
||
if (toData[i] > 100) {
|
||
valNodes[i].setAttribute('y', y - 5);
|
||
valNodes[i].textContent = Math.round(mm);
|
||
valNodes[i].setAttribute('opacity', (t).toFixed(2));
|
||
} else {
|
||
valNodes[i].setAttribute('opacity', '0');
|
||
}
|
||
}
|
||
if (t < 1) animId = requestAnimationFrame(tick);
|
||
}
|
||
animId = requestAnimationFrame(tick);
|
||
}
|
||
|
||
function setScenario(scen) {
|
||
const data = scen === 'weak' ? WEAK : NORMAL;
|
||
const from = current;
|
||
current = data;
|
||
|
||
const total = data.reduce((a, b) => a + b, 0);
|
||
const monsoonSum = MONSOON.reduce((a, i) => a + data[i], 0);
|
||
const share = Math.round((monsoonSum / total) * 100);
|
||
|
||
sharePill.innerHTML = `Juni – September: <strong>${share} %</strong> vom Jahresregen`;
|
||
sharePill.dataset.state = 'warn';
|
||
totalPill.innerHTML = `Ganzes Jahr: <strong>${deAT(total)} mm</strong>`;
|
||
totalPill.dataset.state = scen === 'weak' ? 'bad' : 'good';
|
||
noteEl.textContent = NOTES[scen];
|
||
|
||
root.querySelectorAll('[data-scen]').forEach(b => {
|
||
b.classList.toggle('ge-btn-primary', b.dataset.scen === scen);
|
||
});
|
||
|
||
paint(from, data, scen);
|
||
}
|
||
|
||
root.querySelectorAll('[data-scen]').forEach(b => {
|
||
b.addEventListener('click', () => setScenario(b.dataset.scen));
|
||
});
|
||
|
||
// Startanimation von 0 hoch
|
||
current = NORMAL.map(() => 0);
|
||
setScenario('normal');
|
||
}
|
||
|
||
// Registry — Object.assign, damit Ladereihenfolge egal ist.
|
||
window.GlossarExperiments = Object.assign(window.GlossarExperiments || {}, {
|
||
'monsun': monsunRain
|
||
});
|
||
})();
|