diff --git a/animation/post9-animWebinar.mp4 b/animation/post9-animWebinar.mp4 index 21dfbd8..2a940b7 100644 Binary files a/animation/post9-animWebinar.mp4 and b/animation/post9-animWebinar.mp4 differ diff --git a/post9.html b/post9.html index 25dec40..5b542c7 100644 --- a/post9.html +++ b/post9.html @@ -4552,52 +4552,34 @@ if (location.search.indexOf("nur=anim") >= 0) { kuerzel: "DB", name: "David Bertram", rolle: "Produkt & Design" }, ]; - /* Die vier Teile, Wort fuer Wort von „Was heute kommt“. Die Zeichen sind - die Phosphor-Gegenstuecke zu den Lucide-Zeichen auf derselben Slide: + /* Die vier Teile, Wort fuer Wort von „Was heute kommt“. akzent ist das + Wort in Gelb, wie auf den Kapitel-Slides des Webinars. Die Zeichen sind + die Phosphor-Gegenstuecke zu den Lucide-Zeichen auf „Was heute kommt“: Kanban fuer die Pipelines, ein Ablauf fuer die Automation, zwei Blasen fuer den Chat, das Geschenk fuer die Ueberraschung. */ const WB_TEILE = [ - { rubrik: "Teil 1", zeichen: "kanban", zeilen: ["Von der Anfrage", "zum Auftrag"] }, - { rubrik: "Teil 2", zeichen: "tree-structure", zeilen: ["Das Herzstück:", "Automatisierung"] }, - { rubrik: "Teil 3", zeichen: "chats-circle", zeilen: ["Alles drumherum: Chat,", "Personal, Passwörter"] }, - { rubrik: "Zum Schluss", zeichen: "gift", zeilen: ["Eine Überraschung"] }, + { rubrik: "Teil 1", zeichen: "kanban", akzent: "Auftrag", zeilen: ["Von der Anfrage", "zum Auftrag"] }, + { rubrik: "Teil 2", zeichen: "tree-structure", akzent: "Automatisierung", zeilen: ["Das Herzstück:", "Automatisierung"] }, + { rubrik: "Teil 3", zeichen: "chats-circle", akzent: "drumherum:", zeilen: ["Alles drumherum: Chat,", "Personal, Passwörter"] }, + { rubrik: "Zum Schluss", zeichen: "gift", akzent: "Überraschung", zeilen: ["Eine Überraschung"] }, ]; - /* Rot nur fuer die Live-Marke. Aus ui/theme.css von hivegrid-cloud: - red-100, red-200, red-700 nach dem Muster der Marken in Post 8, der - Punkt ist red-500. */ - const WB_ROT = { flaeche: "#FEE2E2", rand: "#FDCBCB", zeichen: "#C52020", punkt: "#ED4646" }; - /* Das Sechseck der Webinar-Slides: Gelb #FCE48A, Tinte als Rand. */ - const WB_WABE = "#FCE48A"; + /* Markengelb fuer die Wabe in der Live-Marke, wie --accent-yellow. */ + const WB_GELB = "#F1B729"; + /* Die Farben der Kapitel-Slides im Webinar (quelle/slides/teil1.html): + dunkler Grund, Titel fast weiss, die Rubrik grau. */ + const WB_DUNKEL = "#24242B", WB_HELL = "#FAFAFA", WB_GRAU = "#9D9DA0"; const WB_B = 720, WB_RAND = 34, WB_KOPF = 92; let wbNr = 0; - /* Die Live-Marke: Punkt, Wort, und um den Punkt ein Ring, der einmal pro - Sekunde aufgeht. puls laeuft von 0 bis 1; ohne puls steht der Punkt - still. */ - function wbLive(x, ym, puls) { - const gr = 14, h = gr + 13, b = messen("Live", gr, 700) + gr * 1.9 + 14; - let g = ''; - const px = x + 15; - if (puls !== undefined && puls > 0 && puls < 1) { - g += ''; - } - g += ''; - g += uiText(px + 11, ym + gr * .36, "Live", gr, 700, WB_ROT.zeichen); - return g; - } - - /* Der Rahmen: Zeichen, Titel, Live-Marke, Trennlinie. Wie emRahmen in - Post 8, nur ohne Reiter. */ + /* Der Rahmen: Zeichen, Titel, Trennlinie. Wie emRahmen in Post 8, nur + ohne Reiter. Eine eigene Live-Marke gibt es nicht mehr: Der Titel sagt + schon „Live-Webinar“ (Dejan, 20.10.). */ function wbRahmen(h, inhalt, o) { const B = WB_B + 2 * WB_RAND; let g = ikon(o.zeichen, WB_RAND + 14, 33, 20, CHAT_TEXT); g += uiText(WB_RAND + 32, 40, o.titel, 20, 800, CHAT_TEXT); - g += wbLive(WB_RAND + 46 + messen(o.titel, 20, 800), 33, o.puls); /* Rechts der Termin. Ohne ihn sagt das erste Element nur, worum es geht, und nicht, wann. */ if (o.rechts) { @@ -4610,28 +4592,105 @@ if (location.search.indexOf("nur=anim") >= 0) return stapelKasten(B, h, 22, g); } - /* Das Sechseck mit Zeichen, wie auf „Was heute kommt“. */ - function wbWabe(x, y, s, zeichen) { - const k = s / 56; - const p = [[3, 28], [15.5, 6.4], [40.5, 6.4], [53, 28], [40.5, 49.6], [15.5, 49.6]] - .map(q => n1(x + q[0] * k) + "," + n1(y + q[1] * k)).join(" "); - return '' + - ikon(zeichen, x + s / 2, y + s / 2, s * .42, CHAT_TEXT); + /* Das Wabenmuster der Kapitel-Slides (bilder/wabe-ecke.png im Webinar): + gelbe G-Waben, oben rechts am kraeftigsten, nach links unten auslaufend. + Hier als Vektoren nachgebaut, damit jedes Videobild gleich scharf ist + und kein Bild nachladen muss. Die Waben stehen einmal fest; das Auslaufen + macht eine Maske mit Verlauf, die maske() je Bild neu benennt. */ + const WB_MUSTER = (() => { + const G = "384.5,166.5 336.4,83.2 192.2,166.5 288.3,166.5 240.3,249.7 144.2,249.7 96.1,166.5 144.2,83.2 336.4,83.2 298,16.8 288.3,0 96.1,0 0,166.5 96.1,332.9 288.3,332.9"; + const B = 50, S = B / 384.5, H = 332.9 * S, L = 7; + const DX = 0.75 * B + L, DY = H + L * 1.1; + let g = ""; + for (let sp = 0; sp * DX < 720 + B; sp++) { + for (let z = -1; z * DY < 430; z++) { + const x = 720 - B - sp * DX, y = z * DY + (sp % 2 ? DY / 2 : 0); + if (x < 200 - B || y > 320) continue; + g += ''; + } + } + return g; + })(); + /* Das zweite Muster der Webinar-Slides (waben.py, Slides „noah“ und + „problem“): weisse Waben mit grauem Rand auf hellem Grund, dazwischen + einzelne gelbe. Fuer die Folien nach Teil 1, damit die Titelfolie + dunkel bleibt und die anderen sich davon abheben. Spitze Waben wie + dort, die gelben stehen ausserhalb der Maske und bleiben voll. */ + const WB_WABEN_HELL = (() => { + const R = 26, DX = Math.sqrt(3) * R, DY = 1.5 * R; + const ecken = (x, y) => [-90, -30, 30, 90, 150, 210] + .map(w => n1(x + R * Math.cos(w * Math.PI / 180)) + "," + n1(y + R * Math.sin(w * Math.PI / 180))).join(" "); + const zellen = []; + for (let j = 0; j * DY < 330; j++) { + for (let i = 0; i * DX < 720; i++) { + const x = 720 - i * DX - (j % 2 ? DX / 2 : 0), y = j * DY; + if (x > 230) zellen.push([x, y]); + } + } + const naechste = (zx, zy) => zellen.reduce((a, z) => Math.hypot(z[0] - zx, z[1] - zy) < Math.hypot(a[0] - zx, a[1] - zy) ? z : a); + const gelb = [naechste(470, 40), naechste(400, 140), naechste(690, 315)]; + return { + waben: zellen.filter(z => !gelb.includes(z)).map(z => '').join(""), + gelb: gelb.map(z => '').join(""), + }; + })(); + + function wbMaske(id) { + const verlauf = (vid, staerke) => '' + + '' + + ''; + const maske = (mid, vid) => '' + + ''; + return verlauf(id + "v", ".45") + maske(id, id + "v") + verlauf(id + "hv", "1") + maske(id + "h", id + "hv"); } - /* Eine Folie auf der Buehne. Sechseck oben rechts, Rubrik oben links, - der Titel unten links: dieselbe Diagonale wie auf den Kapitel-Slides - des Webinars. */ + /* Das Zeichen des Teils in einer grossen gelben Wabe oben rechts. Sie + sitzt im Muster wie eine hervorgehobene Zelle: Ein Rand in der Farbe + des Grunds raeumt die Waben darunter frei. Auf der dunklen Folie flach + wie die G-Waben, auf den hellen spitz wie die Zellen dort. */ + function wbIkonWabe(zeichen, hell) { + const cx = 620, cy = 118, R = 58, rand = 9; + const winkel = hell ? [-90, -30, 30, 90, 150, 210] : [0, 60, 120, 180, 240, 300]; + const ecken = r => winkel.map(w => n1(cx + r * Math.cos(w * Math.PI / 180)) + "," + + n1(cy + r * Math.sin(w * Math.PI / 180))).join(" "); + return '' + + '' + + ikon(zeichen, cx, cy, R * .86, CHAT_TEXT); + } + + /* Eine Titelzeile, das Akzentwort in Gelb. */ + function wbZeile(x, y, text, akzent, gr, farbe) { + const i = akzent ? text.indexOf(akzent) : -1; + const stueck = (s, farbe) => s ? '' + wfEsc(s) + '' : ""; + const inhalt = i < 0 ? wfEsc(text) : stueck(text.slice(0, i), farbe) + stueck(akzent, WB_GELB) + + stueck(text.slice(i + akzent.length), farbe); + return '' + inhalt + ''; + } + + /* Eine Folie auf der Buehne, im Look der Webinar-Slides: oben rechts das + Wabenmuster, oben links die Rubrik, unten links der Titel mit einem Wort + in Gelb. Teil 1 ist die Titelfolie, dunkel mit G-Waben wie die + Kapitel-Slides. Die drei danach sind hell mit dem zweiten Muster + (Dejan, 20.10.). */ const WB_BUEHNE_H = 430; - function wbFolie(x0, teil) { - let g = wbWabe(x0 + WB_B - 44 - 124, 36, 124, teil.zeichen); - g += uiText(x0 + 48, 78, teil.rubrik.toUpperCase(), 17, 800, CHAT_TEXT3, { sperr: "0.13em" }); + function wbFolie(x0, teil, maske, hell) { + let g = ''; + if (hell) { + g += ''; + g += '' + WB_WABEN_HELL.waben + ''; + g += '' + WB_WABEN_HELL.gelb + ''; + } else { + g += ''; + g += '' + WB_MUSTER + ''; + } + g += wbIkonWabe(teil.zeichen, hell); + g += uiText(48, 78, teil.rubrik.toUpperCase(), 17, 800, hell ? CHAT_TEXT3 : WB_GRAU, { sperr: "0.13em" }); const zh = 56, unten = WB_BUEHNE_H - 94; teil.zeilen.forEach((z, i) => { - g += uiText(x0 + 48, unten - (teil.zeilen.length - 1 - i) * zh, z, 48, 800, CHAT_TEXT); + g += wbZeile(48, unten - (teil.zeilen.length - 1 - i) * zh, z, teil.akzent, 48, hell ? CHAT_TEXT : WB_HELL); }); - return g; + return g + ''; } /* Eine Kachel fuer einen Sprecher: Foto, Name, Rolle. spricht blendet den @@ -4668,22 +4727,19 @@ if (location.search.indexOf("nur=anim") >= 0) sich je Teil ein Abschnitt der Leiste. Unter der Buehne die beiden Sprecher. Wer redet, hat den dunklen Rand. - Die Reihenfolge ist die aus dem Skript: Jedes Kapitel eroeffnet Noah, - dann uebernimmt David. + Noah die erste Haelfte, David die zweite. Alles, was sich wiederholt, laeuft in ganzen Perioden ueber die acht - Sekunden: der Puls der Live-Marke achtmal, die Balken drei-, vier- und - fuenfmal je Teil. Ein Video, das sich auf Instagram - wiederholt, springt dann nur bei der Folie und nicht beim Puls. */ + Sekunden, die Balken drei-, vier- und fuenfmal je Teil. Ein Video, das + sich auf Instagram wiederholt, springt dann nur bei der Folie. */ function animWebinar(j, t) { const n = WB_TEILE.length, abschnitt = 1 / n, schub = .045; /* Welche Folie steht, und wie weit der Schub zur naechsten ist. */ let g = ""; - const id = "wbbuehne" + (++wbNr); + const id = "wbbuehne" + (++wbNr), maske = id + "m"; g += ''; - g += ''; + '"/>' + wbMaske(maske); + g += ''; let folien = ""; for (let i = 0; i < n; i++) { @@ -4693,27 +4749,33 @@ if (location.search.indexOf("nur=anim") >= 0) const raus_ = i === n - 1 ? 0 : daempfen(stufe(t, (i + 1) * abschnitt - schub, (i + 1) * abschnitt + schub)); const x0 = (1 - rein) * WB_B - raus_ * WB_B; if (x0 >= WB_B || x0 <= -WB_B) continue; - folien += wbFolie(x0, WB_TEILE[i]); + folien += wbFolie(x0, WB_TEILE[i], maske, i > 0); } g += '' + folien + ''; /* Die Leiste: vier Abschnitte, jeder fuellt sich, solange sein Teil steht. */ const LX = 48, LB = WB_B - 96, LL = 8, sb = (LB - (n - 1) * LL) / n, ly = WB_BUEHNE_H - 40; + /* Die Spur ist auf der dunklen Titelfolie weiss und durchscheinend, auf + den hellen grau. Sie wechselt mit dem Schub zu Teil 2. */ + const hellAnteil = daempfen(stufe(t, abschnitt - schub, abschnitt + schub)); for (let i = 0; i < n; i++) { const sx = LX + i * (sb + LL); g += ''; + '" height="6" rx="3" fill="#FFFFFF" fill-opacity="' + (.16 * (1 - hellAnteil)).toFixed(3) + '"/>'; + if (hellAnteil > 0) g += ''; const p = stufe(t, i * abschnitt, (i + 1) * abschnitt); if (p > 0) g += ''; + '" height="6" rx="3" fill="' + WB_GELB + '"/>'; } - /* Die Sprecher. Im Abschnitt eines Teils redet erst Noah, ab etwas - ueber der Haelfte David. Der Wechsel blendet ueber, damit nicht beide + /* Die Sprecher. Noah redet bei Teil 1 und 2, David bei Teil 3 und 4. + Gewechselt wird nur einmal, mit dem Schub zur dritten Folie, und am + Ende des Loops zurueck. Vorher wechselte es in jedem Teil, das war zu + unruhig (Dejan, 20.10.). Der Wechsel blendet ueber, damit nicht beide Raender gleichzeitig hart umspringen. */ - const imTeil = (t % abschnitt) / abschnitt; - const david = stufe(imTeil, .52, .62); + const david = daempfen(stufe(t, .5 - schub, .5 + schub)); const noah = 1 - david; const sy = WB_BUEHNE_H + 20, kb = (WB_B - 16) / 2; const phase = t * n; @@ -4721,7 +4783,7 @@ if (location.search.indexOf("nur=anim") >= 0) g += wbSprecher(kb + 16, sy, kb, WB_SPRECHER[1], david, phase); const h = WB_KOPF + WB_BUEHNE_H + 20 + 84 + WB_RAND; - return wbRahmen(h, g, { zeichen: "video-camera", titel: "Live-Webinar", puls: (t * 8) % 1, + return wbRahmen(h, g, { zeichen: "video-camera", titel: "Live-Webinar", rechts: WEBINAR.datum + ", " + WEBINAR.uhrzeit + " Uhr" }); }