Files
geograsim/App/assets/js/glossar-experiments-monsun.js
T
Adminator 685b4700f8 Glossar: 4 erklärende Widgets + 2 Einträge anschaulicher
Widgets: Ernährungspyramide (US-1992 vs DACH-2024 vs Denkfehler), Kakao-Bohne
(Schoko-Fließband + Mischverhältnis→Sorte/kaputte Masse), Klimazone (Bänder Äquator→Pol
mit Beispielpflanzen), Monsun (Monatsregen-Balkendiagramm Mumbai + Wien-Vergleich, Quelle IMD).
Texte: off-season-import (Winter-Erdbeere ~10× CO₂, Schiff vs Flug) + lebensmittelverschwendung
(70 kg = ~6 Wochen Essen; 700 Mio. Hungernde) — greifbare Vergleiche.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 01:42:20 +02:00

195 lines
8.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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): JuniSeptember
const MONSOON = [5, 6, 7, 8];
// Regen pro Monat in mm.
// Normal: Mumbai (Colaba) 19812010, 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 JunSep
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
});
})();