Post 9: Folien im Look der Webinar-Slides, ruhigerer Sprecherwechsel
Teil 1 dunkel mit G-Waben wie die Kapitel-Slides, die drei danach hell mit dem zweiten Wabenmuster. Die Zeichen sitzen als grosse gelbe Wabe im Muster, je ein Wort im Titel in Gelb. Die Live-Marke entfaellt, der Titel sagt es schon. Noah spricht bei Teil 1 und 2, David bei 3 und 4. Video neu exportiert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
78a6fcba78
commit
cd74224880
2 files changed
+128
-66
No files matched your search
Binary file not shown.
+128
-66
@@ -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 = '<rect x="' + n1(x) + '" y="' + n1(ym - h / 2) + '" width="' + n1(b) +
|
||||
'" height="' + h + '" rx="' + n1(h / 2) + '" fill="' + WB_ROT.flaeche +
|
||||
'" stroke="' + WB_ROT.rand + '" stroke-width="1.2"/>';
|
||||
const px = x + 15;
|
||||
if (puls !== undefined && puls > 0 && puls < 1) {
|
||||
g += '<circle cx="' + n1(px) + '" cy="' + n1(ym) + '" r="' + n1(4 + 6 * raus(puls)) +
|
||||
'" fill="' + WB_ROT.punkt + '" opacity="' + n1(.35 * (1 - puls)) + '"/>';
|
||||
}
|
||||
g += '<circle cx="' + n1(px) + '" cy="' + n1(ym) + '" r="4" fill="' + WB_ROT.punkt + '"/>';
|
||||
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 '<polygon points="' + p + '" fill="' + WB_WABE + '" stroke="' + CHAT_TEXT +
|
||||
'" stroke-width="1.6" stroke-linejoin="round"/>' +
|
||||
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 += '<polygon points="' + G + '" transform="translate(' + n1(x) + ' ' + n1(y) + ') scale(' + S.toFixed(5) + ')"/>';
|
||||
}
|
||||
}
|
||||
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 => '<polygon points="' + ecken(z[0], z[1]) + '"/>').join(""),
|
||||
gelb: gelb.map(z => '<polygon points="' + ecken(z[0], z[1]) + '"/>').join(""),
|
||||
};
|
||||
})();
|
||||
|
||||
function wbMaske(id) {
|
||||
const verlauf = (vid, staerke) => '<linearGradient id="' + vid + '" gradientUnits="userSpaceOnUse" x1="720" y1="0" x2="400" y2="300">' +
|
||||
'<stop offset="0" stop-color="#FFFFFF" stop-opacity="' + staerke + '"/><stop offset="1" stop-color="#FFFFFF" stop-opacity="0"/>' +
|
||||
'</linearGradient>';
|
||||
const maske = (mid, vid) => '<mask id="' + mid + '" maskUnits="userSpaceOnUse" x="0" y="0" width="720" height="' + WB_BUEHNE_H + '">' +
|
||||
'<rect width="720" height="' + WB_BUEHNE_H + '" fill="url(#' + vid + ')"/></mask>';
|
||||
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 '<polygon points="' + ecken(R + rand) + '" fill="' + (hell ? FL_GRUND : WB_DUNKEL) + '"/>' +
|
||||
'<polygon points="' + ecken(R) + '" fill="' + WB_GELB + '"/>' +
|
||||
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 ? '<tspan fill="' + farbe + '">' + wfEsc(s) + '</tspan>' : "";
|
||||
const inhalt = i < 0 ? wfEsc(text) : stueck(text.slice(0, i), farbe) + stueck(akzent, WB_GELB) +
|
||||
stueck(text.slice(i + akzent.length), farbe);
|
||||
return '<text x="' + n1(x) + '" y="' + n1(y) + '" font-family="Manrope, sans-serif" font-size="' + gr +
|
||||
'" font-weight="800" fill="' + farbe + '">' + inhalt + '</text>';
|
||||
}
|
||||
|
||||
/* 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 = '<g transform="translate(' + n1(x0) + ',0)">';
|
||||
if (hell) {
|
||||
g += '<rect width="' + WB_B + '" height="' + WB_BUEHNE_H + '" fill="' + FL_GRUND + '"/>';
|
||||
g += '<g mask="url(#' + maske + 'h)" fill="#FFFFFF" stroke="#D6D6D9" stroke-width="1.5">' + WB_WABEN_HELL.waben + '</g>';
|
||||
g += '<g fill="' + WB_GELB + '">' + WB_WABEN_HELL.gelb + '</g>';
|
||||
} else {
|
||||
g += '<rect width="' + WB_B + '" height="' + WB_BUEHNE_H + '" fill="' + WB_DUNKEL + '"/>';
|
||||
g += '<g mask="url(#' + maske + ')" fill="' + WB_GELB + '">' + WB_MUSTER + '</g>';
|
||||
}
|
||||
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 + '</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 += '<clipPath id="' + id + '"><path d="' + rundRechteck(0, 0, WB_B, WB_BUEHNE_H, 14) +
|
||||
'"/></clipPath>';
|
||||
g += '<path d="' + rundRechteck(0, 0, WB_B, WB_BUEHNE_H, 14) + '" fill="' + FL_GRUND +
|
||||
'" stroke="' + CHAT_RAND + '" stroke-width="1.2"/>';
|
||||
'"/></clipPath>' + wbMaske(maske);
|
||||
g += '<path d="' + rundRechteck(0, 0, WB_B, WB_BUEHNE_H, 14) + '" fill="' + WB_DUNKEL + '"/>';
|
||||
|
||||
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 += '<g clip-path="url(#' + id + ')">' + folien + '</g>';
|
||||
|
||||
/* 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 += '<rect x="' + n1(sx) + '" y="' + ly + '" width="' + n1(sb) +
|
||||
'" height="6" rx="3" fill="#DADADC"/>';
|
||||
'" height="6" rx="3" fill="#FFFFFF" fill-opacity="' + (.16 * (1 - hellAnteil)).toFixed(3) + '"/>';
|
||||
if (hellAnteil > 0) g += '<rect x="' + n1(sx) + '" y="' + ly + '" width="' + n1(sb) +
|
||||
'" height="6" rx="3" fill="#DADADC" fill-opacity="' + hellAnteil.toFixed(3) + '"/>';
|
||||
const p = stufe(t, i * abschnitt, (i + 1) * abschnitt);
|
||||
if (p > 0) g += '<rect x="' + n1(sx) + '" y="' + ly + '" width="' + n1(Math.max(6, sb * p)) +
|
||||
'" height="6" rx="3" fill="' + CHAT_TEXT + '"/>';
|
||||
'" 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" });
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user