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>
This commit is contained in:
2026-07-17 01:42:20 +02:00
parent 283780994b
commit 685b4700f8
6 changed files with 889 additions and 0 deletions
+194
View File
@@ -0,0 +1,194 @@
/**
* 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
});
})();