Post 9 Variante 4/5: Noah gespiegelt, Wabengrafiken animiert

- Titelslide: Noah an x 800 gespiegelt, er schaut zum Text
- Slide 2 und 3 als Video (6 s, 1:1): die gelben Waben ploppen
  nacheinander auf und ziehen sich zum Schleifenende zurück
- Im Streifen laufen die Animationen mit, Standbild bleibt der Endstand

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
dejanandClaude Opus 5.5 committed 2026-11-02 19:22:27 +01:00
1 parent 5b768537a2
commit 466b094c0b
6 files changed
+144 -28

No files matched your search

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+72 -14
View File
@@ -1481,7 +1481,18 @@ if (location.search.indexOf("nur=anim") >= 0)
die Titelslide getragen haette. */
/* Variante 2 hat kein Bewegtbild: alle fuenf Elemente sind Standbilder.
Ein reines Bild-Carousel darf als Anzeige auch 4:5 bleiben. */
const ANIMS = [];
/* Variante 4: Slide 2 und 3 laufen als Video (Dejan, 02.11.2026). Im
Carousel stehen sie an Stelle 2 und 3, die Standbilder im Streifen
zeigen ihren Endstand. Mit Video bleibt die Anzeige im Instagram-Feed
1:1 und laeuft nicht in Reels (rules.md 5). */
const ANIMS = [
Object.assign({ key: "animBuerokratie", type: "feature", dauer: 6, eyebrow: "Bürokratie",
layouts: [{ id: "standard", label: "Standard" }], vis: "v4buerokratieAnim" },
{ head: "", body: "", visW: 1080, visLeft: 0, visBottom: 0, quadrat: { visW: 1080, visLeft: 0, visBottom: 0 } }),
Object.assign({ key: "animKi", type: "feature", dauer: 6, eyebrow: "Künstliche Intelligenz",
layouts: [{ id: "standard", label: "Standard" }], vis: "v4kiAnim" },
{ head: "", body: "", visW: 1080, visLeft: 0, visBottom: 0, quadrat: { visW: 1080, visLeft: 0, visBottom: 0 } }),
];
/* Vorlage für eine selbst angelegte Slide. Sie startet bewusst mit Text
@@ -4903,6 +4914,7 @@ if (location.search.indexOf("nur=anim") >= 0)
const h = r * Math.sqrt(3) / 2;
const pkt = [[cx + r, cy], [cx + r / 2, cy + h], [cx - r / 2, cy + h], [cx - r, cy], [cx - r / 2, cy - h], [cx + r / 2, cy - h]];
return '<polygon points="' + pkt.map(p => n1(p[0]) + "," + n1(p[1])).join(" ") + '" fill="' + fill + '"' +
(o.deck != null ? ' fill-opacity="' + Math.max(0, Math.min(1, o.deck)).toFixed(3) + '"' : "") +
(o.stroke ? ' stroke="' + o.stroke + '" stroke-width="' + (o.sw || 1.5) + '"' : "") +
(o.dreh ? ' transform="rotate(' + o.dreh + " " + n1(cx) + " " + n1(cy) + ')"' : "") + "/>";
}
@@ -5214,21 +5226,58 @@ if (location.search.indexOf("nur=anim") >= 0)
gezaehlt wird spaltenweise von links. Dejan, 02.11.2026: weniger Zahlen,
mehr Grafik, aber nichts erfunden. Die Grafik zeigt nur den Prozentwert
der Quelle, auf ganze Waben gerundet. */
function v4Hundert(y0, art) {
function v4Hundert(y0, art, zeit) {
const r = 28, sx = 1.5 * r, w = r * Math.sqrt(3);
const x0 = V2_RAND + r + (1080 - 2 * V2_RAND - (19 * sx + 2 * r)) / 2;
let g = "";
let g = "", oben = "";
for (let i = 0; i < 100; i++) {
const sp = Math.floor(i / 5), z = i % 5;
const cx = x0 + sp * sx, cy = y0 + w / 2 + z * w + (sp % 2 ? w / 2 : 0);
const a = art(i);
g += a === "voll" ? v2Wabe(cx, cy, r - 4, V4.gelb)
: a === "rand" ? v2Wabe(cx + 0, cy, r - 6, "none", { stroke: V4.gelb, sw: 3 })
: v2Wabe(cx, cy, r - 4, V4.spurWabe);
/* p: wie weit diese Wabe schon da ist, 0 bis 1. Ohne zeit (Standbild)
ist alles fertig. Die graue Wabe liegt immer darunter, die gelbe
waechst darueber auf, mit leichtem Ueberschwingen (rueck). Unter
einer umrandeten Wabe verblasst das Grau, sobald sie steht. */
const p = a && zeit ? zeit(i) : 1;
g += v2Wabe(cx, cy, r - 4, V4.spurWabe, a === "rand" ? { deck: 1 - p } : null);
if (!a || p <= 0) continue;
const sk = rueck(p);
oben += a === "voll" ? v2Wabe(cx, cy, (r - 4) * sk, V4.gelb)
: v2Wabe(cx, cy, (r - 6) * sk, "none", { stroke: V4.gelb, sw: 3 });
}
return g;
return g + oben;
}
/* Im Streifen (Uebersicht, Vergleichsseite) laufen Slide 2 und 3 als
Animation mit, damit man sieht, was ins Video kommt. Der Editor und der
Bildexport bleiben beim Endstand. 30 Bilder je Sekunde reichen. */
function v4StreifenAnimieren() {
if (!NUR_STREIFEN) return;
const start = performance.now();
let zuletzt = 0;
const schritt = jetzt => {
if (jetzt - zuletzt > 33) {
zuletzt = jetzt;
const t = ((jetzt - start) / 1000 % V4_DAUER) / V4_DAUER;
document.querySelectorAll(".vis.vis-v4buerokratie").forEach(v => { v.innerHTML = v4Buerokratie(t); });
document.querySelectorAll(".vis.vis-v4ki").forEach(v => { v.innerHTML = v4Ki(t); });
}
requestAnimationFrame(schritt);
};
requestAnimationFrame(schritt);
}
/* Der Zeitverlauf der animierten Karten, t von 0 bis 1 ueber V4_DAUER
Sekunden. Die Waben kommen nacheinander (start, abstand, je dauer), am
Ende ziehen sich alle zusammen zurueck, damit die Schleife auf
Instagram weich neu ansetzt statt zu springen. */
const V4_DAUER = 6;
const v4Welle = (t, k, start, abstand, dauer) => {
const rein = stufe(t, start + k * abstand, start + k * abstand + dauer);
const raus = 1 - daempfen(stufe(t, 0.92, 1));
return rein * raus;
};
/* 1 · Titel. Noah freigestellt vor der grossen gelben Wabe, wie in
Variante 2 auf der Zitat-Karte: Er traegt Schwarz und braucht das Gelb
dahinter. Die Karte teasert das Webinar, die Zahlen kommen danach. */
@@ -5239,7 +5288,9 @@ if (location.search.indexOf("nur=anim") >= 0)
g += v4Struktur();
g += v2Wabe(800, H - 170, 330, V4.gelb);
const pb = 1000, ph = pb * 1049 / 1600;
g += v2Bild(800 - pb * 0.475, H - ph, pb, ph, "./assets/team/noah-person.webp", { passen: "meet" });
/* Gespiegelt an x 800 (Dejan, 02.11.2026): Noah schaut so zum Text. */
g += '<g transform="translate(1600,0) scale(-1,1)">' +
v2Bild(800 - pb * 0.475, H - ph, pb, ph, "./assets/team/noah-person.webp", { passen: "meet" }) + "</g>";
g += v2Bild(V2_RAND, 86, 42 * 3.8, 42, "./assets/brand/logo-white.svg", { passen: "meet" });
g += v2Chamfer(V2_RAND, 186, 236, 50, 10, V2.gelbHell);
g += uiText(V2_RAND + 118, 219, "LIVE-WEBINAR", 20, 800, V2.gelbText, { anker: "middle", sperr: "0.14em" });
@@ -5255,11 +5306,13 @@ if (location.search.indexOf("nur=anim") >= 0)
/* 2 · Buerokratie. KfW: im mittelstaendischen Bau 8,1 % der Arbeitszeit.
Als Bild: 100 Waben, 8 gelb. */
function v4Buerokratie() {
function v4Buerokratie(t) {
v4Ton();
let g = v4Kopf("Zeitfresser Bürokratie", "Im Bau: 8 von 100 Arbeitsstunden", "gehen für Bürokratie drauf.");
/* Grafik und Legende stehen mittig zwischen Kopf (bis 306) und Fuss (ab 952). */
g += v4Hundert(456, i => i >= 92 ? "voll" : "");
/* Animiert: die 8 gelben ab 0,6 s nacheinander, je 0,5 s. */
g += v4Hundert(456, i => i >= 92 ? "voll" : "",
t == null ? null : i => v4Welle(t, i - 92, 0.10, 0.035, 0.08));
g += v2Wabe(V2_RAND + 13, 777, 13, V4.gelb);
g += uiText(V2_RAND + 40, 785, "Jede Wabe ist eine Arbeitsstunde. Gelb ist Bürokratie.", 24, 600, V4.text2);
g += v4Fuss(["Quelle: KfW-Mittelstandspanel, KfW Research Nr. 495, April 2025.",
@@ -5269,10 +5322,12 @@ if (location.search.indexOf("nur=anim") >= 0)
/* 3 · KI. Bitkom: 4 % nutzen KI, 9 % planen es. Dasselbe Bild, jede Wabe
ein Betrieb. */
function v4Ki() {
function v4Ki(t) {
v4Ton();
let g = v4Kopf("Künstliche Intelligenz", "KI ist im Handwerk kaum angekommen.", "4 von 100 Betrieben nutzen sie.");
g += v4Hundert(456, i => i < 4 ? "voll" : i < 13 ? "rand" : "");
/* Animiert: erst die 4 vollen, dann die 9 umrandeten. */
g += v4Hundert(456, i => i < 4 ? "voll" : i < 13 ? "rand" : "",
t == null ? null : i => i < 4 ? v4Welle(t, i, 0.10, 0.05, 0.08) : v4Welle(t, i - 4, 0.40, 0.03, 0.07));
g += v2Wabe(V2_RAND + 13, 777, 13, V4.gelb);
g += uiText(V2_RAND + 40, 785, "nutzen KI", 24, 600, V4.text2);
g += v2Wabe(V2_RAND + 213, 777, 11, "none", { stroke: V4.gelb, sw: 3 });
@@ -5375,8 +5430,10 @@ if (location.search.indexOf("nur=anim") >= 0)
anmeldung: () => visAnmeldung(),
animWebinar: (j, t) => animWebinar(j, t),
v4titel: () => v4Titel(),
v4buerokratie: () => v4Buerokratie(),
v4ki: () => v4Ki(),
v4buerokratie: () => v4Buerokratie(null),
v4buerokratieAnim: (j, t) => v4Buerokratie(t),
v4ki: () => v4Ki(null),
v4kiAnim: (j, t) => v4Ki(t),
v4zeit: () => v4Zeit(),
v4webinar: () => v4Webinar(),
v2betrieb: () => v2Betrieb(),
@@ -8573,6 +8630,7 @@ if (location.search.indexOf("nur=anim") >= 0)
weiter oben hat genau das ausgeloest -- "Cannot set properties of
null" beim Freischalten der Export-Knoepfe. */
animNurModus();
v4StreifenAnimieren();
} catch (err) {
console.error(err);
setStatus("Fehler beim Laden: " + err.message, "err");
+72 -14
View File
@@ -1481,7 +1481,18 @@ if (location.search.indexOf("nur=anim") >= 0)
die Titelslide getragen haette. */
/* Variante 2 hat kein Bewegtbild: alle fuenf Elemente sind Standbilder.
Ein reines Bild-Carousel darf als Anzeige auch 4:5 bleiben. */
const ANIMS = [];
/* Variante 4: Slide 2 und 3 laufen als Video (Dejan, 02.11.2026). Im
Carousel stehen sie an Stelle 2 und 3, die Standbilder im Streifen
zeigen ihren Endstand. Mit Video bleibt die Anzeige im Instagram-Feed
1:1 und laeuft nicht in Reels (rules.md 5). */
const ANIMS = [
Object.assign({ key: "animBuerokratie", type: "feature", dauer: 6, eyebrow: "Bürokratie",
layouts: [{ id: "standard", label: "Standard" }], vis: "v4buerokratieAnim" },
{ head: "", body: "", visW: 1080, visLeft: 0, visBottom: 0, quadrat: { visW: 1080, visLeft: 0, visBottom: 0 } }),
Object.assign({ key: "animKi", type: "feature", dauer: 6, eyebrow: "Künstliche Intelligenz",
layouts: [{ id: "standard", label: "Standard" }], vis: "v4kiAnim" },
{ head: "", body: "", visW: 1080, visLeft: 0, visBottom: 0, quadrat: { visW: 1080, visLeft: 0, visBottom: 0 } }),
];
/* Vorlage für eine selbst angelegte Slide. Sie startet bewusst mit Text
@@ -4903,6 +4914,7 @@ if (location.search.indexOf("nur=anim") >= 0)
const h = r * Math.sqrt(3) / 2;
const pkt = [[cx + r, cy], [cx + r / 2, cy + h], [cx - r / 2, cy + h], [cx - r, cy], [cx - r / 2, cy - h], [cx + r / 2, cy - h]];
return '<polygon points="' + pkt.map(p => n1(p[0]) + "," + n1(p[1])).join(" ") + '" fill="' + fill + '"' +
(o.deck != null ? ' fill-opacity="' + Math.max(0, Math.min(1, o.deck)).toFixed(3) + '"' : "") +
(o.stroke ? ' stroke="' + o.stroke + '" stroke-width="' + (o.sw || 1.5) + '"' : "") +
(o.dreh ? ' transform="rotate(' + o.dreh + " " + n1(cx) + " " + n1(cy) + ')"' : "") + "/>";
}
@@ -5214,21 +5226,58 @@ if (location.search.indexOf("nur=anim") >= 0)
gezaehlt wird spaltenweise von links. Dejan, 02.11.2026: weniger Zahlen,
mehr Grafik, aber nichts erfunden. Die Grafik zeigt nur den Prozentwert
der Quelle, auf ganze Waben gerundet. */
function v4Hundert(y0, art) {
function v4Hundert(y0, art, zeit) {
const r = 28, sx = 1.5 * r, w = r * Math.sqrt(3);
const x0 = V2_RAND + r + (1080 - 2 * V2_RAND - (19 * sx + 2 * r)) / 2;
let g = "";
let g = "", oben = "";
for (let i = 0; i < 100; i++) {
const sp = Math.floor(i / 5), z = i % 5;
const cx = x0 + sp * sx, cy = y0 + w / 2 + z * w + (sp % 2 ? w / 2 : 0);
const a = art(i);
g += a === "voll" ? v2Wabe(cx, cy, r - 4, V4.gelb)
: a === "rand" ? v2Wabe(cx + 0, cy, r - 6, "none", { stroke: V4.gelb, sw: 3 })
: v2Wabe(cx, cy, r - 4, V4.spurWabe);
/* p: wie weit diese Wabe schon da ist, 0 bis 1. Ohne zeit (Standbild)
ist alles fertig. Die graue Wabe liegt immer darunter, die gelbe
waechst darueber auf, mit leichtem Ueberschwingen (rueck). Unter
einer umrandeten Wabe verblasst das Grau, sobald sie steht. */
const p = a && zeit ? zeit(i) : 1;
g += v2Wabe(cx, cy, r - 4, V4.spurWabe, a === "rand" ? { deck: 1 - p } : null);
if (!a || p <= 0) continue;
const sk = rueck(p);
oben += a === "voll" ? v2Wabe(cx, cy, (r - 4) * sk, V4.gelb)
: v2Wabe(cx, cy, (r - 6) * sk, "none", { stroke: V4.gelb, sw: 3 });
}
return g;
return g + oben;
}
/* Im Streifen (Uebersicht, Vergleichsseite) laufen Slide 2 und 3 als
Animation mit, damit man sieht, was ins Video kommt. Der Editor und der
Bildexport bleiben beim Endstand. 30 Bilder je Sekunde reichen. */
function v4StreifenAnimieren() {
if (!NUR_STREIFEN) return;
const start = performance.now();
let zuletzt = 0;
const schritt = jetzt => {
if (jetzt - zuletzt > 33) {
zuletzt = jetzt;
const t = ((jetzt - start) / 1000 % V4_DAUER) / V4_DAUER;
document.querySelectorAll(".vis.vis-v4buerokratie").forEach(v => { v.innerHTML = v4Buerokratie(t); });
document.querySelectorAll(".vis.vis-v4ki").forEach(v => { v.innerHTML = v4Ki(t); });
}
requestAnimationFrame(schritt);
};
requestAnimationFrame(schritt);
}
/* Der Zeitverlauf der animierten Karten, t von 0 bis 1 ueber V4_DAUER
Sekunden. Die Waben kommen nacheinander (start, abstand, je dauer), am
Ende ziehen sich alle zusammen zurueck, damit die Schleife auf
Instagram weich neu ansetzt statt zu springen. */
const V4_DAUER = 6;
const v4Welle = (t, k, start, abstand, dauer) => {
const rein = stufe(t, start + k * abstand, start + k * abstand + dauer);
const raus = 1 - daempfen(stufe(t, 0.92, 1));
return rein * raus;
};
/* 1 · Titel. Noah freigestellt vor der grossen gelben Wabe, wie in
Variante 2 auf der Zitat-Karte: Er traegt Schwarz und braucht das Gelb
dahinter. Die Karte teasert das Webinar, die Zahlen kommen danach. */
@@ -5239,7 +5288,9 @@ if (location.search.indexOf("nur=anim") >= 0)
g += v4Struktur();
g += v2Wabe(800, H - 170, 330, V4.gelb);
const pb = 1000, ph = pb * 1049 / 1600;
g += v2Bild(800 - pb * 0.475, H - ph, pb, ph, "./assets/team/noah-person.webp", { passen: "meet" });
/* Gespiegelt an x 800 (Dejan, 02.11.2026): Noah schaut so zum Text. */
g += '<g transform="translate(1600,0) scale(-1,1)">' +
v2Bild(800 - pb * 0.475, H - ph, pb, ph, "./assets/team/noah-person.webp", { passen: "meet" }) + "</g>";
g += v2Bild(V2_RAND, 86, 42 * 3.8, 42, "./assets/brand/logo-white.svg", { passen: "meet" });
g += v2Chamfer(V2_RAND, 186, 236, 50, 10, V2.gelbHell);
g += uiText(V2_RAND + 118, 219, "LIVE-WEBINAR", 20, 800, V2.gelbText, { anker: "middle", sperr: "0.14em" });
@@ -5255,11 +5306,13 @@ if (location.search.indexOf("nur=anim") >= 0)
/* 2 · Buerokratie. KfW: im mittelstaendischen Bau 8,1 % der Arbeitszeit.
Als Bild: 100 Waben, 8 gelb. */
function v4Buerokratie() {
function v4Buerokratie(t) {
v4Ton();
let g = v4Kopf("Zeitfresser Bürokratie", "Im Bau: 8 von 100 Arbeitsstunden", "gehen für Bürokratie drauf.");
/* Grafik und Legende stehen mittig zwischen Kopf (bis 306) und Fuss (ab 952). */
g += v4Hundert(456, i => i >= 92 ? "voll" : "");
/* Animiert: die 8 gelben ab 0,6 s nacheinander, je 0,5 s. */
g += v4Hundert(456, i => i >= 92 ? "voll" : "",
t == null ? null : i => v4Welle(t, i - 92, 0.10, 0.035, 0.08));
g += v2Wabe(V2_RAND + 13, 777, 13, V4.gelb);
g += uiText(V2_RAND + 40, 785, "Jede Wabe ist eine Arbeitsstunde. Gelb ist Bürokratie.", 24, 600, V4.text2);
g += v4Fuss(["Quelle: KfW-Mittelstandspanel, KfW Research Nr. 495, April 2025.",
@@ -5269,10 +5322,12 @@ if (location.search.indexOf("nur=anim") >= 0)
/* 3 · KI. Bitkom: 4 % nutzen KI, 9 % planen es. Dasselbe Bild, jede Wabe
ein Betrieb. */
function v4Ki() {
function v4Ki(t) {
v4Ton();
let g = v4Kopf("Künstliche Intelligenz", "KI ist im Handwerk kaum angekommen.", "4 von 100 Betrieben nutzen sie.");
g += v4Hundert(456, i => i < 4 ? "voll" : i < 13 ? "rand" : "");
/* Animiert: erst die 4 vollen, dann die 9 umrandeten. */
g += v4Hundert(456, i => i < 4 ? "voll" : i < 13 ? "rand" : "",
t == null ? null : i => i < 4 ? v4Welle(t, i, 0.10, 0.05, 0.08) : v4Welle(t, i - 4, 0.40, 0.03, 0.07));
g += v2Wabe(V2_RAND + 13, 777, 13, V4.gelb);
g += uiText(V2_RAND + 40, 785, "nutzen KI", 24, 600, V4.text2);
g += v2Wabe(V2_RAND + 213, 777, 11, "none", { stroke: V4.gelb, sw: 3 });
@@ -5375,8 +5430,10 @@ if (location.search.indexOf("nur=anim") >= 0)
anmeldung: () => visAnmeldung(),
animWebinar: (j, t) => animWebinar(j, t),
v4titel: () => v4Titel(),
v4buerokratie: () => v4Buerokratie(),
v4ki: () => v4Ki(),
v4buerokratie: () => v4Buerokratie(null),
v4buerokratieAnim: (j, t) => v4Buerokratie(t),
v4ki: () => v4Ki(null),
v4kiAnim: (j, t) => v4Ki(t),
v4zeit: () => v4Zeit(),
v4webinar: () => v4Webinar(),
v2betrieb: () => v2Betrieb(),
@@ -8576,6 +8633,7 @@ if (location.search.indexOf("nur=anim") >= 0)
weiter oben hat genau das ausgeloest -- "Cannot set properties of
null" beim Freischalten der Export-Knoepfe. */
animNurModus();
v4StreifenAnimieren();
} catch (err) {
console.error(err);
setStatus("Fehler beim Laden: " + err.message, "err");