diff --git a/animation/post9-v4-animBuerokratie-1x1.mp4 b/animation/post9-v4-animBuerokratie-1x1.mp4 new file mode 100644 index 0000000..8dc6621 Binary files /dev/null and b/animation/post9-v4-animBuerokratie-1x1.mp4 differ diff --git a/animation/post9-v4-animKi-1x1.mp4 b/animation/post9-v4-animKi-1x1.mp4 new file mode 100644 index 0000000..8afda13 Binary files /dev/null and b/animation/post9-v4-animKi-1x1.mp4 differ diff --git a/animation/post9-v5-animBuerokratie-1x1.mp4 b/animation/post9-v5-animBuerokratie-1x1.mp4 new file mode 100644 index 0000000..200bfd9 Binary files /dev/null and b/animation/post9-v5-animBuerokratie-1x1.mp4 differ diff --git a/animation/post9-v5-animKi-1x1.mp4 b/animation/post9-v5-animKi-1x1.mp4 new file mode 100644 index 0000000..66a4acc Binary files /dev/null and b/animation/post9-v5-animKi-1x1.mp4 differ diff --git a/post9-v4.html b/post9-v4.html index ef4be52..4db8e25 100644 --- a/post9-v4.html +++ b/post9-v4.html @@ -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 '"; } @@ -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 += '' + + v2Bild(800 - pb * 0.475, H - ph, pb, ph, "./assets/team/noah-person.webp", { passen: "meet" }) + ""; 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"); diff --git a/post9-v5.html b/post9-v5.html index ca53234..022164e 100644 --- a/post9-v5.html +++ b/post9-v5.html @@ -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 '"; } @@ -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 += '' + + v2Bild(800 - pb * 0.475, H - ph, pb, ph, "./assets/team/noah-person.webp", { passen: "meet" }) + ""; 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");