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:
@@ -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): 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
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user