Startseite entschlackt: weniger Text, ruhiger, besser am Handy

- Cursor-Effekt im Hero entfernt (Messfadenkreuz, Temperatur-Skala,
  Maus-Wärmespur); Wärmebild läuft nur noch ruhig im Hintergrund
- Preloader, Laufbänder, Intro, Ablauf-Block, Kennzahlen, Footer-CTA,
  kursive Akzentwörter, Verlaufstext und Nummern-Pillen entfernt
- Texte in allen Abschnitten deutlich gekürzt, schlichte Überschriften
- Leistungen: auf Handy/Tablet wischbare Kärtchen mit Zähler und Pfeilen
- Neue, zusammenhängende Illustrationen für die fünf Leistungs-Kärtchen
- Heizungs-Finder ohne Prozent-Balken, kürzere Ergebnistexte
- Energie-Haus: Tabs als Wischleiste am Handy, kein Autoplay
- GSAP, ScrollTrigger, SplitText, Lenis und Instrument Serif entfernt
- Barrierefreiheit: Kontrast, Fokusring, Fokus im Kartenstapel,
  Telefon-Button mit Namen, Menü sperrt Hintergrund
- Cache-Busting (?v=20261004) für CSS und JS, README aktualisiert

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
NotPika69andClaude Opus 5.5 committed 2026-10-04 20:14:53 +02:00
1 parent 717a7de501
commit ad155124b1
20 files changed
+1182 -2147

No files matched your search

+16 -131
View File
@@ -1,15 +1,13 @@
/* ==========================================================================
Olzinger – „Wärmebild“ (WebGL)
Ein thermografisches Feld mit Isothermen. Die Maus wirkt als Wärmequelle
und hinterlässt eine Wärmespur, die langsam abkühlt und sich ausbreitet.
Hinter Fließtext wird die Wärme gedämpft, damit alles lesbar bleibt.
Ein ruhiges thermografisches Feld mit Isothermen als Hero-Hintergrund.
Eine langsam wandernde Wärmequelle hält das Bild lebendig – ganz ohne
Maus- oder Touch-Interaktion. Hinter Fließtext wird die Wärme gedämpft.
========================================================================== */
(function (w) {
'use strict';
var MAX_HEAT = 14;
var T_MIN = 14; // °C bei Feldwert 0
var T_MAX = 28; // °C bei Feldwert 1
var VERT = 'attribute vec2 aPos; void main(){ gl_Position = vec4(aPos, 0.0, 1.0); }';
@@ -87,32 +85,6 @@
'}'
].join('\n');
/* ---------- CPU-Gegenstück des Feldes (für die Temperaturanzeige) ---------- */
function fract(x) { return x - Math.floor(x); }
function smoothstep(a, b, x) { var t = Math.min(1, Math.max(0, (x - a) / (b - a))); return t * t * (3 - 2 * t); }
function hash(x, y) {
x = fract(x * 123.34); y = fract(y * 456.21);
var dd = x * (x + 45.32) + y * (y + 45.32);
x += dd; y += dd;
return fract(x * y);
}
function noise(x, y) {
var ix = Math.floor(x), iy = Math.floor(y), fx = x - ix, fy = y - iy;
var ux = fx * fx * (3 - 2 * fx), uy = fy * fy * (3 - 2 * fy);
var a = hash(ix, iy), b = hash(ix + 1, iy), c = hash(ix, iy + 1), d = hash(ix + 1, iy + 1);
var ab = a + (b - a) * ux, cd = c + (d - c) * ux;
return ab + (cd - ab) * uy;
}
function fbm(x, y) {
var v = 0, a = 0.5;
for (var i = 0; i < 5; i++) {
v += a * noise(x, y);
var nx = 1.6 * x - 1.2 * y, ny = 1.2 * x + 1.6 * y;
x = nx; y = ny; a *= 0.5;
}
return v;
}
function compile(gl, type, src) {
var sh = gl.createShader(type);
gl.shaderSource(sh, src);
@@ -161,8 +133,6 @@
var uHeat = gl.getUniformLocation(prog, 'uHeat');
var reduce = !!opts.reduceMotion;
var finePointer = w.matchMedia('(hover: hover) and (pointer: fine)').matches;
var probe = opts.probe, probeVal = opts.probeVal, marker = opts.legendMarker;
var calmEl = opts.calm || null;
var heatData = new Float32Array(MAX_HEAT * 4);
@@ -171,12 +141,11 @@
var quality = (w.devicePixelRatio || 1) > 1.5 ? 0.62 : 0.85;
var width = 0, height = 0, rect = null;
var running = false, visible = true, raf = 0;
var time = Math.random() * 40, last = 0, frame = 0;
var time = Math.random() * 40, last = 0;
var intro = reduce ? 1 : 0, introTarget = reduce ? 1 : 0;
var frameTimes = [], tuned = false;
// Cursor in Feldkoordinaten: x ∈ [0, Seitenverhältnis], y ∈ [0, 1] (y nach oben)
var cur = { x: 0, y: 0, tx: 0, ty: 0, inside: false, lastMove: -1e9, heat: 0, clientX: 0, clientY: 0 };
// Feldkoordinaten: x ∈ [0, Seitenverhältnis], y ∈ [0, 1] (y nach oben)
var auto = { heat: 0 };
var lastEmit = 0;
@@ -204,10 +173,7 @@
gl.viewport(0, 0, width, height);
}
measureCalm();
}
function toField(clientX, clientY) {
return { x: (clientX - rect.left) / rect.height, y: 1 - (clientY - rect.top) / rect.height };
if (reduce) start(); // Größenänderung leert die Leinwand – Standbild neu zeichnen
}
function emit(x, y, i, r) {
@@ -215,75 +181,6 @@
if (trail.length > MAX_HEAT - 2) trail.pop();
}
function onMove(e) {
if (e.pointerType && e.pointerType !== 'mouse') return;
rect = canvas.getBoundingClientRect();
var f = toField(e.clientX, e.clientY);
cur.tx = f.x; cur.ty = f.y;
cur.clientX = e.clientX - rect.left;
cur.clientY = e.clientY - rect.top;
if (!cur.inside) { cur.x = f.x; cur.y = f.y; }
cur.inside = true;
cur.lastMove = performance.now();
if (probe) probe.classList.add('is-on');
}
function onLeave() {
cur.inside = false;
if (probe) probe.classList.remove('is-on');
}
function onTouch(e) {
var t = e.touches && e.touches[0];
if (!t) return;
rect = canvas.getBoundingClientRect();
var f = toField(t.clientX, t.clientY);
emit(f.x, f.y, 0.85, 0.09);
}
if (finePointer) {
hero.addEventListener('pointermove', onMove, { passive: true });
hero.addEventListener('pointerleave', onLeave, { passive: true });
}
hero.addEventListener('touchstart', onTouch, { passive: true });
function calmAt(px, py) {
var a = smoothstep(calmRect[0] - 0.08, calmRect[0] + 0.02, px) * smoothstep(calmRect[1] - 0.08, calmRect[1] + 0.02, py);
var b = (1 - smoothstep(calmRect[2] - 0.02, calmRect[2] + 0.08, px)) * (1 - smoothstep(calmRect[3] - 0.02, calmRect[3] + 0.08, py));
return a * b;
}
// Exakt dieselbe Rechnung wie im Shader – nur für einen Punkt
function fieldAt(px, py) {
var asp = width / height, t = time;
var sx = px * 1.35, sy = py * 1.35;
var qx = fbm(sx, sy + t * 0.05), qy = fbm(sx + 5.2, sy - t * 0.04);
var rx = fbm(sx + 2.4 * qx + 1.7 + t * 0.06, sy + 2.4 * qy + 9.2 + t * 0.06);
var ry = fbm(sx + 2.4 * qx + 8.3 - t * 0.05, sy + 2.4 * qy + 2.8 - t * 0.05);
var f = fbm(sx + 2.2 * rx, sy + 2.2 * ry);
var base = smoothstep(0.18, 0.86, f) * 0.46;
var zx = px - asp * 0.8, zy = py - 0.76;
base += 0.36 * Math.exp(-(zx * zx + zy * zy) / 0.11) * (0.8 + 0.2 * Math.sin(t * 0.45));
var cx = px - asp * 0.16, cy = py - 0.12;
base -= 0.12 * Math.exp(-(cx * cx + cy * cy) / 0.2);
base *= 0.5 + 0.5 * smoothstep(0, 0.95, (px / asp) * 0.85 + py * 0.55);
var heat = 0;
for (var i = 0; i < MAX_HEAT; i++) {
var o = i * 4, dx = px - heatData[o], dy = py - heatData[o + 1];
heat += heatData[o + 2] * Math.exp(-(dx * dx + dy * dy) / Math.max(heatData[o + 3] * heatData[o + 3], 0.0001));
}
heat *= 1 - 0.62 * calmAt(px, py);
var v = Math.max(base, 0) + heat * 0.66;
v = 1 - Math.exp(-v * 1.35);
return v * intro;
}
function readTemperature() {
if (!probeVal || !cur.inside) return;
var v = Math.max(0, Math.min(1, fieldAt(cur.tx, cur.ty)));
var temp = T_MIN + v * (T_MAX - T_MIN);
probeVal.textContent = temp.toFixed(1).replace('.', ',') + ' °C';
if (marker) marker.style.left = (v * 100).toFixed(1) + '%';
}
function step(now) {
raf = 0;
if (!running) return;
@@ -304,22 +201,14 @@
intro += (introTarget - intro) * (1 - Math.exp(-dt * 1.8));
var asp = width / height;
// Maus
var k = 1 - Math.exp(-dt * 9);
cur.x += (cur.tx - cur.x) * k;
cur.y += (cur.ty - cur.y) * k;
var idle = (now - cur.lastMove) > 3200;
cur.heat += ((cur.inside ? 1 : 0) - cur.heat) * (1 - Math.exp(-dt * 3));
if (cur.inside && !idle && now - lastEmit > 55) {
emit(cur.x, cur.y, 0.42, 0.075);
lastEmit = now;
}
// Autopilot: wandernde Wärmequelle oben rechts, solange niemand interagiert
var autoOn = (!cur.inside || idle) && !reduce;
// Langsam wandernde Wärmequelle oben rechts
var autoOn = !reduce;
auto.heat += ((autoOn ? 0.8 : 0) - auto.heat) * (1 - Math.exp(-dt * 1.2));
var ax = asp * (0.7 + 0.16 * Math.sin(time * 0.21)) + 0.05 * Math.sin(time * 0.53);
var ay = 0.66 + 0.16 * Math.sin(time * 0.29 + 1.3);
// Hochkant (Handy): Wärme am rechten Rand halten, damit die Überschrift lesbar bleibt
var portrait = asp < 1;
var ax = portrait ? asp * (1.0 + 0.04 * Math.sin(time * 0.21)) + 0.03 * Math.sin(time * 0.53)
: asp * (0.7 + 0.16 * Math.sin(time * 0.21)) + 0.05 * Math.sin(time * 0.53);
var ay = portrait ? 0.62 + 0.12 * Math.sin(time * 0.29 + 1.3) : 0.66 + 0.16 * Math.sin(time * 0.29 + 1.3);
if (autoOn && now - lastEmit > 140) {
emit(ax, ay, 0.3 * auto.heat, 0.09);
lastEmit = now;
@@ -331,7 +220,7 @@
trail[i].r += dt * 0.032;
}
heatData[0] = cur.x; heatData[1] = cur.y; heatData[2] = cur.heat * 0.8; heatData[3] = 0.095;
heatData[0] = 0; heatData[1] = 0; heatData[2] = 0; heatData[3] = 0.1;
heatData[4] = ax; heatData[5] = ay; heatData[6] = auto.heat * 0.7; heatData[7] = 0.13;
for (var j = 0; j < MAX_HEAT - 2; j++) {
var o = (j + 2) * 4, tp = trail[j];
@@ -346,12 +235,8 @@
gl.uniform4fv(uHeat, heatData);
gl.drawArrays(gl.TRIANGLES, 0, 3);
frame++;
if (frame % 4 === 0) readTemperature();
if (probe && cur.inside) {
probe.style.transform = 'translate3d(' + cur.clientX.toFixed(1) + 'px,' + cur.clientY.toFixed(1) + 'px,0)';
}
// Reduzierte Bewegung: Standbild – einmal zeichnen, dann ruhen
if (reduce) { running = false; return; }
raf = w.requestAnimationFrame(step);
}