Post 9: Format 1:1 für die Anzeige und vier neue Varianten
- Format-Schalter 4:5 / 1:1 in Post 9, Stand auf 1:1 (Meta nimmt ein Carousel mit Video als Anzeige nur quadratisch), Video in 1:1 - animation.mjs baut das Format der Seite, Datei mit -1x1 - Variante 2/3: Kundengeschichte nach dem weclapp-Karussell, hell und dunkel - Variante 4/5: Zahlen-Karussell nach HERO Insights mit KfW- und Bitkom-Zahlen, Noah als Teaser, hell und dunkel - post9-varianten.html zeigt alle Varianten untereinander - Menüs und Übersicht um die Varianten ergänzt, Regel zu 1:1 in rules.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
cafefc9f1b
commit
5b768537a2
21 files changed
+34393
-49
No files matched your search
+23
-6
@@ -3,6 +3,7 @@
|
||||
*
|
||||
* node animation.mjs post6.html alle animierten Slides
|
||||
* node animation.mjs post6.html 5 nur Slide 6 (nullbasiert: 5)
|
||||
* node animation.mjs post9.html animWebinar 1x1 im Format 1:1
|
||||
*
|
||||
* WARUM MP4 UND NICHT GIF
|
||||
* Instagram nimmt kein GIF an. Was im Feed als "kurzer animierter Beitrag"
|
||||
@@ -14,7 +15,11 @@
|
||||
* DIE WERTE
|
||||
* Behaelter MP4, H.264 High, yuv420p
|
||||
* Bild 1080 x 1350, also 4:5 wie unsere Standbilder -- ein Carousel
|
||||
* schneidet alle Elemente auf das Format des ersten zu
|
||||
* schneidet alle Elemente auf das Format des ersten zu.
|
||||
* Kennt die Seite ein zweites Format (window.SLIDE_FORMAT, bisher
|
||||
* nur Post 9 mit 1:1 fuer die Anzeige), gilt dessen Hoehe. Das
|
||||
* Format kommt aus dem Projektstand oder als drittes Argument,
|
||||
* die Datei traegt dann ein Kennzeichen (-1x1).
|
||||
* Bildrate 30 fps
|
||||
* Laenge 3 s ist das Minimum, 60 s je Element das Maximum; wir liegen
|
||||
* bei 5
|
||||
@@ -40,12 +45,16 @@ const SEITE = process.argv[2] || "post6.html";
|
||||
Slide mehr und hat deshalb auch keine Position im Streifen, die sich
|
||||
verschieben koennte. Ohne Angabe werden alle des Projekts gebaut. */
|
||||
const NUR = process.argv[3] || null;
|
||||
/* Format, wenn die Seite mehrere kennt. Ohne Angabe gilt der Projektstand. */
|
||||
const FORMAT = process.argv[4] || null;
|
||||
const ZUSATZ = FORMAT ? "&format=" + encodeURIComponent(FORMAT) : "";
|
||||
const PROJEKT = SEITE.replace(/\.html$/, "");
|
||||
|
||||
const CHROME = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome";
|
||||
const PORT = 9530;
|
||||
const FPS = 30;
|
||||
const BREITE = 1080, HOEHE = 1350;
|
||||
const BREITE = 1080;
|
||||
let HOEHE = 1350, DATEI_ZUSATZ = "";
|
||||
const DATENRATE = "12M";
|
||||
const ZIEL = path.join(process.cwd(), "animation");
|
||||
|
||||
@@ -138,8 +147,16 @@ try {
|
||||
|
||||
// Erst die Liste holen. Ohne k rendert der Modus nichts, das Skript laeuft
|
||||
// aber vollstaendig durch und window.ANIM_LISTE steht bereit.
|
||||
await laden(SEITE + "?nur=anim");
|
||||
await laden(SEITE + "?nur=anim" + ZUSATZ);
|
||||
const alle = await cdp.werte("window.ANIM_LISTE ? window.ANIM_LISTE() : null");
|
||||
/* Das Format der Seite. Posts ohne zweites Format melden nichts und
|
||||
bleiben bei 4:5. */
|
||||
const fmt = await cdp.werte("window.SLIDE_FORMAT ? window.SLIDE_FORMAT() : null");
|
||||
if (fmt) {
|
||||
HOEHE = fmt.hoehe; DATEI_ZUSATZ = fmt.datei || "";
|
||||
await cdp.ruf("Emulation.setDeviceMetricsOverride",
|
||||
{ width: BREITE, height: HOEHE, deviceScaleFactor: 1, mobile: false });
|
||||
}
|
||||
if (!alle) throw new Error(SEITE + " kennt keine Animationen (window.ANIM_LISTE fehlt)");
|
||||
const liste = alle.filter(a => NUR == null || a.key === NUR);
|
||||
if (!liste.length) throw new Error("keine Animation" +
|
||||
@@ -150,7 +167,7 @@ try {
|
||||
|
||||
for (const a of liste) {
|
||||
const anzahl = Math.round(a.dauer * FPS);
|
||||
await laden(SEITE + "?nur=anim&k=" + encodeURIComponent(a.key));
|
||||
await laden(SEITE + "?nur=anim&k=" + encodeURIComponent(a.key) + ZUSATZ);
|
||||
|
||||
/* Nachsehen, dass wirklich ein Blatt dasteht und es die richtigen Masse
|
||||
hat. Ein leeres Fenster faellt sonst erst am fertigen Video auf. */
|
||||
@@ -188,7 +205,7 @@ try {
|
||||
"UMLAUT_ERLAUBT. Einen Geviertstrich ersetzt ein Punkt oder ein Komma.");
|
||||
|
||||
const ordner = await mkdtemp(path.join(tmpdir(), "postgrid-bilder-"));
|
||||
process.stdout.write(` ${a.key}: ${anzahl} Bilder `);
|
||||
process.stdout.write(` ${a.key}: ${anzahl} Bilder, ${BREITE} x ${HOEHE} `);
|
||||
for (let n = 0; n < anzahl; n++) {
|
||||
/* Das letzte Bild ist nicht t = 1, sondern eins davor: bei einer
|
||||
Schleife waeren t = 0 und t = 1 dasselbe Bild und es haekelte kurz. */
|
||||
@@ -198,7 +215,7 @@ try {
|
||||
Buffer.from(r.data, "base64"));
|
||||
if (n % 15 === 0) process.stdout.write(".");
|
||||
}
|
||||
const datei = path.join(ZIEL, PROJEKT + "-" + a.key + ".mp4");
|
||||
const datei = path.join(ZIEL, PROJEKT + "-" + a.key + DATEI_ZUSATZ + ".mp4");
|
||||
process.stdout.write(" kodiere ");
|
||||
await kodieren(ordner, datei, anzahl);
|
||||
await rm(ordner, { recursive: true, force: true });
|
||||
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 350 KiB |
+6
-1
@@ -805,7 +805,12 @@ if (location.search.indexOf("nur=streifen") >= 0)
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
|
||||
+6
-1
@@ -1108,7 +1108,12 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
|
||||
+6
-1
@@ -1001,7 +1001,12 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
|
||||
+6
-1
@@ -1021,7 +1021,12 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
|
||||
+6
-1
@@ -1049,7 +1049,12 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
|
||||
+6
-1
@@ -1049,7 +1049,12 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
|
||||
+6
-1
@@ -1053,7 +1053,12 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
|
||||
+6
-1
@@ -1063,7 +1063,12 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
|
||||
+6
-1
@@ -1063,7 +1063,12 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
|
||||
+8403
File diff suppressed because it is too large.
Load diff
+8407
File diff suppressed because it is too large.
Load diff
+8584
File diff suppressed because it is too large.
Load diff
+8587
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,179 @@
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PostGrid · Post 9 · Varianten</title>
|
||||
<link rel="stylesheet" href="./assets/fonts/manrope.css">
|
||||
<style>
|
||||
/* ─── Post 9 im Vergleich ─────────────────────────────────────────────────
|
||||
Die Varianten der Webinar-Anzeige untereinander, wie die Uebersicht aller
|
||||
Projekte: jede Variante kommt als iframe im Modus ?nur=streifen und zeigt
|
||||
genau, was der Generator zeigt. Variante 1 beginnt mit dem Bewegtbild.
|
||||
Das steht nicht im Streifen (Animationen liegen ausserhalb), deshalb
|
||||
laeuft davor das exportierte MP4 im passenden Format. */
|
||||
* { box-sizing: border-box; }
|
||||
:root { color-scheme: dark; }
|
||||
html, body { height: 100%; }
|
||||
body {
|
||||
margin: 0; font-family: Manrope, system-ui, sans-serif;
|
||||
background: #101116; color: #C7CCD6; overflow: hidden;
|
||||
}
|
||||
.menubar {
|
||||
position: relative; z-index: 10; display: flex; align-items: center; gap: 2px;
|
||||
height: 34px; padding: 0 10px; background: #16181F;
|
||||
border-bottom: 1px solid #23262F; font-size: 13px; user-select: none;
|
||||
}
|
||||
.menubar .marke { font-weight: 800; color: #FFFFFF; margin-right: 14px; letter-spacing: -.01em; }
|
||||
.menubar a {
|
||||
color: #C7CCD6; text-decoration: none; padding: 5px 10px; border-radius: 6px;
|
||||
}
|
||||
.menubar a:hover { background: #23262F; color: #FFFFFF; }
|
||||
.stage { position: absolute; inset: 34px 0 0; overflow-y: auto; overflow-x: hidden;
|
||||
background-color: #101116;
|
||||
background-image: radial-gradient(circle, #282B35 1.6px, transparent 1.6px);
|
||||
background-size: 26px 26px;
|
||||
}
|
||||
.flaeche { padding: 48px 54px 80px; display: flex; flex-direction: column; gap: 56px; }
|
||||
.kopf h1 { margin: 0; font-size: 26px; font-weight: 800; color: #FFFFFF; letter-spacing: -.01em; }
|
||||
.kopf p { margin: 6px 0 0; font-size: 14px; color: #6E7480; }
|
||||
.variante { display: flex; flex-direction: column; gap: 14px; }
|
||||
.variante h2 {
|
||||
margin: 0; font-size: 13px; font-weight: 800; letter-spacing: .14em;
|
||||
text-transform: uppercase; color: #6E7480;
|
||||
}
|
||||
.variante h2 b { color: #F1B729; font-weight: 800; }
|
||||
.variante h2 span { color: #FFFFFF; }
|
||||
.variante p { margin: 0; max-width: 820px; font-size: 14px; line-height: 1.55; color: #9AA0AC; }
|
||||
.reihe { display: flex; gap: 0; align-items: flex-start; }
|
||||
.rahmen { overflow: hidden; background: #FFFFFF; border: 1px solid #2E313B; flex: none; }
|
||||
.rahmen + .rahmen { border-left: 0; }
|
||||
.rahmen iframe {
|
||||
display: block; border: 0; transform-origin: 0 0; background: #FFFFFF; pointer-events: none;
|
||||
}
|
||||
.rahmen video { display: block; width: 100%; height: 100%; object-fit: cover; }
|
||||
.marke-video {
|
||||
font-size: 11px; color: #6E7480; letter-spacing: .06em; text-transform: uppercase; margin-top: 8px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="menubar">
|
||||
<span class="marke">PostGrid</span>
|
||||
<a href="./uebersicht.html">Alle Projekte</a>
|
||||
<a href="./post9.html">Variante 1 bearbeiten</a>
|
||||
<a href="./post9-v2.html">Variante 2 bearbeiten</a>
|
||||
<a href="./post9-v3.html">Variante 3 bearbeiten</a>
|
||||
<a href="./post9-v4.html">Variante 4 bearbeiten</a>
|
||||
<a href="./post9-v5.html">Variante 5 bearbeiten</a>
|
||||
</div>
|
||||
|
||||
<main class="stage" id="stage">
|
||||
<div class="flaeche">
|
||||
<div class="kopf">
|
||||
<h1>Post 9 · Webinar-Anzeige</h1>
|
||||
<p id="meta">Fünf Varianten derselben Anzeige. Lade Streifen ...</p>
|
||||
</div>
|
||||
|
||||
<section class="variante">
|
||||
<h2><b>Variante 1</b> · <span>Webinar-Fenster</span> · fertig seit 17.10.</h2>
|
||||
<p>Das Bewegtbild zeigt das Webinar mit seinen vier Teilen und beiden Sprechern, danach die Anmeldung.
|
||||
Zwei Elemente, das erste ist ein Video. Als Anzeige deshalb in 1:1.</p>
|
||||
<div class="reihe" id="reihe1">
|
||||
<div class="rahmen" id="video1"><video id="anim1" autoplay muted loop playsinline></video></div>
|
||||
<div class="rahmen"><iframe id="f1" src="./post9.html?nur=streifen" loading="eager"></iframe></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="variante">
|
||||
<h2><b>Variante 2</b> · <span>Kundengeschichte</span> · angelehnt an weclapp</h2>
|
||||
<p>Komposition wie das Karussell von weclapp zu PV Green (Werbebibliothek, ID 792638160291622): Logo mit Etikett
|
||||
und Foto, Zitat mit freigestellter Person, Ausgangslage, mit HiveGrid, Anmeldung. Farben, Schrift und Formen
|
||||
von uns: Gelb und Dunkel, Manrope, Waben, Chamfer. Fünf Bilder, kein Video, als Anzeige also auch in 4:5 möglich.</p>
|
||||
<div class="reihe">
|
||||
<div class="rahmen"><iframe id="f2" src="./post9-v2.html?nur=streifen" loading="eager"></iframe></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="variante">
|
||||
<h2><b>Variante 3</b> · <span>Kundengeschichte dunkel</span> · wie Variante 2</h2>
|
||||
<p>Dieselben fünf Slides wie Variante 2 auf dem dunklen Grund des Posts. Texte in Weiß, Formen und Wabenband in
|
||||
einem helleren Blau, Gelb bleibt. Was auf Gelb und im Geerkens-Chip steht, bleibt dunkel.</p>
|
||||
<div class="reihe">
|
||||
<div class="rahmen"><iframe id="f3" src="./post9-v3.html?nur=streifen" loading="eager"></iframe></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="variante">
|
||||
<h2><b>Variante 4</b> · <span>Zahlen</span> · angelehnt an HERO Insights</h2>
|
||||
<p>Komposition wie das Karussell „HERO Insights“ (Werbebibliothek, ID 3818182885142025): dunkles Titelbild,
|
||||
dann Karten mit Titel, schwarzem und gelbem Balken und einer Grafik. Titelbild mit Noah als Teaser fürs Webinar,
|
||||
Zahlen als Wabenbild (100 Waben) und Ring statt Tabellen. Thema: Zeit, die Betriebe verlieren, und wie wenig KI bisher
|
||||
genutzt wird. Zahlen aus KfW Research (April 2025) und Bitkom (504 Betriebe, 2025), Quelle auf jeder Karte.</p>
|
||||
<div class="reihe">
|
||||
<div class="rahmen"><iframe id="f4" src="./post9-v4.html?nur=streifen" loading="eager"></iframe></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="variante">
|
||||
<h2><b>Variante 5</b> · <span>Zahlen dunkel</span> · wie Variante 4</h2>
|
||||
<p>Dieselben fünf Karten wie Variante 4 auf dem dunklen Grund des Posts.</p>
|
||||
<div class="reihe">
|
||||
<div class="rahmen"><iframe id="f5" src="./post9-v5.html?nur=streifen" loading="eager"></iframe></div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
"use strict";
|
||||
const stage = document.getElementById("stage");
|
||||
const meta = document.getElementById("meta");
|
||||
/* Breite und Hoehe melden die Streifen selbst, sobald sie stehen. */
|
||||
const st = {
|
||||
f1: { el: document.getElementById("f1"), w: 1080, h: 1080, da: false },
|
||||
f2: { el: document.getElementById("f2"), w: 5400, h: 1080, da: false },
|
||||
f3: { el: document.getElementById("f3"), w: 5400, h: 1080, da: false },
|
||||
f4: { el: document.getElementById("f4"), w: 5400, h: 1080, da: false },
|
||||
f5: { el: document.getElementById("f5"), w: 5400, h: 1080, da: false },
|
||||
};
|
||||
const video = document.getElementById("anim1"), videoRahmen = document.getElementById("video1");
|
||||
|
||||
window.addEventListener("message", e => {
|
||||
const d = e.data;
|
||||
if (!d || d.hg !== "streifen") return;
|
||||
const r = Object.values(st).find(x => x.el.contentWindow === e.source);
|
||||
if (!r) return;
|
||||
r.w = d.w; r.h = d.h; r.da = true;
|
||||
legen();
|
||||
});
|
||||
|
||||
/* Alle Slides im selben Massstab, eingepasst an die laengste Reihe. */
|
||||
function legen() {
|
||||
const breit = Math.max(st.f1.w + 1080, st.f2.w, st.f3.w, st.f4.w, st.f5.w);
|
||||
const k = Math.min((stage.clientWidth - 108) / breit, 0.42);
|
||||
Object.values(st).forEach(r => {
|
||||
r.el.style.width = r.w + "px";
|
||||
r.el.style.height = r.h + "px";
|
||||
r.el.style.transform = "scale(" + k + ")";
|
||||
r.el.parentElement.style.width = Math.round(r.w * k) + "px";
|
||||
r.el.parentElement.style.height = Math.round(r.h * k) + "px";
|
||||
});
|
||||
/* Das Video im Format der Variante 1: 1:1 oder 4:5. */
|
||||
videoRahmen.style.width = Math.round(1080 * k) + "px";
|
||||
videoRahmen.style.height = Math.round(st.f1.h * k) + "px";
|
||||
const quelle = "./animation/post9-animWebinar" + (st.f1.h === 1080 ? "-1x1" : "") + ".mp4";
|
||||
if (!video.src.endsWith(quelle.slice(1))) { video.src = quelle; video.play().catch(() => {}); }
|
||||
const fertig = Object.values(st).every(r => r.da);
|
||||
meta.textContent = fertig
|
||||
? "Fünf Varianten derselben Anzeige · Variante 1: 1 Video, 1 Bild · Varianten 2 bis 5: je 5 Bilder · Format " +
|
||||
Object.values(st).map(r => r.h === 1080 ? "1:1" : "4:5").join(", ")
|
||||
: "Fünf Varianten derselben Anzeige. Lade Streifen ...";
|
||||
}
|
||||
window.addEventListener("resize", legen);
|
||||
legen();
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+134
-32
@@ -15,7 +15,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
</script>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>PostGrid Post 9 · Webinar</title>
|
||||
<title>PostGrid Post 9 · Variante 1 · Webinar-Fenster</title>
|
||||
<link rel="stylesheet" href="./assets/fonts/manrope.css">
|
||||
<style>
|
||||
/* ─── HiveGrid design tokens (mirrored from app/globals.css) ─────────────
|
||||
@@ -247,6 +247,9 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
#el-inhalt input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
|
||||
#el-inhalt input:focus { outline: none; border-color: var(--accent-yellow); }
|
||||
#el-inhalt .hint { color: #6E7480; margin-top: 6px; }
|
||||
.group[data-grp="format"] .hint {
|
||||
font-size: 11.5px; line-height: 1.5; color: #6E7480; margin-top: 8px;
|
||||
}
|
||||
#el-inhalt .row { margin-top: 0; }
|
||||
.std { margin-bottom: 22px; }
|
||||
.std summary {
|
||||
@@ -345,9 +348,11 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
.strip[data-view="einzeln"] .holder { display: none; }
|
||||
.strip[data-view="einzeln"] .holder.sel { display: block; }
|
||||
|
||||
/* ─── Slide base — exactly Instagram 4:5 (1080 × 1350) ───────────────── */
|
||||
/* ─── Slide base — Instagram 4:5 (1080 × 1350) oder 1:1 (1080 × 1080) ──
|
||||
Die Hoehe kommt aus --slide-h, das formatAnwenden() auf <html> setzt.
|
||||
Ohne Wert bleibt es 4:5 wie in allen anderen Posts. */
|
||||
.slide {
|
||||
width: 1080px; height: 1350px;
|
||||
width: 1080px; height: var(--slide-h, 1350px);
|
||||
position: relative; overflow: hidden;
|
||||
font-family: 'Manrope', system-ui, sans-serif;
|
||||
}
|
||||
@@ -533,7 +538,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
--animskala: .215;
|
||||
flex: none; position: relative; overflow: hidden; border-radius: 6px;
|
||||
width: calc(1080px * var(--animskala));
|
||||
height: calc(1350px * var(--animskala));
|
||||
height: calc(var(--slide-h, 1350px) * var(--animskala));
|
||||
box-shadow: 0 0 0 1px #2E313B;
|
||||
}
|
||||
.animbuehne > .slide {
|
||||
@@ -626,7 +631,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
left: calc(var(--slide-i, 0) * -1080px);
|
||||
width: var(--sheet-w, 5400px); z-index: 1;
|
||||
}
|
||||
.page { position: absolute; top: 0; width: 1080px; height: 1350px; }
|
||||
.page { position: absolute; top: 0; width: 1080px; height: var(--slide-h, 1350px); }
|
||||
|
||||
/* Im Theme modern traegt das Badge wieder Platz, deshalb beginnt der Block
|
||||
bei 92 -- dem Rand der Safe Zone -- statt bei den 59 des Themes notebook. */
|
||||
@@ -1053,7 +1058,12 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-gehe="./uebersicht.html">Alle Projekte im Streifen</button>
|
||||
<hr>
|
||||
@@ -1115,7 +1125,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
<div class="arbeitsflaeche">
|
||||
|
||||
<aside class="panel">
|
||||
<h1>Post 9 · Webinar</h1>
|
||||
<h1>Post 9 · Variante 1</h1>
|
||||
<p class="sub"><span id="kopfzeile">Carousel · 1080 × 1350 px</span><span class="saved" id="saved"></span><br><button class="forget" id="forget">Projekt zurücksetzen</button></p>
|
||||
|
||||
<div class="group">
|
||||
@@ -1145,6 +1155,17 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
Die Datei landet unverändert unter <b>assets/eigene/</b> und gehört mit in den Commit.</div>
|
||||
</details>
|
||||
|
||||
<details class="group" data-grp="format" open>
|
||||
<summary>Format</summary>
|
||||
<div class="opts" id="formate">
|
||||
<button data-g="4x5" aria-pressed="true">4:5 · Beitrag</button>
|
||||
<button data-g="1x1" aria-pressed="false">1:1 · Anzeige</button>
|
||||
</div>
|
||||
<div class="hint">Meta nimmt ein Carousel mit Video als Anzeige nur in 1:1.
|
||||
Beide Formate teilen Texte und Slides. In 1:1 sind die Karten kleiner, und
|
||||
„Link in der Bio“ fehlt, weil die Anzeige einen eigenen Knopf hat.</div>
|
||||
</details>
|
||||
|
||||
<details class="group" data-grp="variante">
|
||||
<summary>Variante</summary>
|
||||
<div class="opts" id="variants">
|
||||
@@ -1434,6 +1455,11 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
head: 'Live zuschauen,<br><span class="a">live fragen.</span>',
|
||||
body: "Was Automatisierung in deinem Betrieb wirklich macht. Mit dem Branchenprogramm, das du schon hast.",
|
||||
vis: "anmeldung", visW: 896, visLeft: 92, visBottom: 168,
|
||||
/* 1:1 fuer die Anzeige: 270 px weniger Hoehe. Ueberschrift und Text
|
||||
bleiben, die Karte wird kleiner und steht mittig, unten wie oben
|
||||
92 px Rand. Gemessen am 02.11.: Text endet bei y 369, die Karte
|
||||
beginnt 50 px darunter. */
|
||||
quadrat: { visW: 736, visLeft: 172, visBottom: 92 },
|
||||
},
|
||||
];
|
||||
|
||||
@@ -1456,6 +1482,10 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
/* 244 statt 226 wie in Post 8: die Karte ist hier niedriger, und der
|
||||
Abstand zur Ueberschrift soll derselbe bleiben, rund 118 px. */
|
||||
vis: "animWebinar", visW: 896, visLeft: 92, visBottom: 244,
|
||||
/* 1:1: die Karte so breit wie auf der Anmelde-Slide, damit beim
|
||||
Wischen nichts springt, und rund 90 px unter der Ueberschrift wie
|
||||
in 4:5 (dort 88). */
|
||||
quadrat: { visW: 736, visLeft: 172, visBottom: 106 },
|
||||
},
|
||||
];
|
||||
|
||||
@@ -4826,9 +4856,14 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
|
||||
g += '<path d="' + rundRechteck(0, y, WB_B, 68, 12) + '" fill="#24242B"/>';
|
||||
g += uiText(WB_B / 2, y + 42, "Jetzt anmelden", 22, 800, "#FFFFFF", { anker: "middle" });
|
||||
y += 68 + 36;
|
||||
g += uiText(WB_B / 2, y, "Link in der Bio", 18, 600, CHAT_TEXT3, { anker: "middle" });
|
||||
y += 6;
|
||||
y += 68;
|
||||
/* „Link in der Bio“ nur im Beitrag. Die Anzeige (1:1) traegt ihren
|
||||
eigenen Knopf zur Anmeldeseite, dort fuehrte die Zeile in die Irre. */
|
||||
if (format !== "1x1") {
|
||||
y += 36;
|
||||
g += uiText(WB_B / 2, y, "Link in der Bio", 18, 600, CHAT_TEXT3, { anker: "middle" });
|
||||
y += 6;
|
||||
}
|
||||
|
||||
return wbRahmen(WB_KOPF + y + WB_RAND, g, { zeichen: "calendar-check", titel: "Live-Webinar" });
|
||||
}
|
||||
@@ -4961,7 +4996,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
if (key === "mock") return SLIDES[i].mockW || 1090;
|
||||
if (key === "mock-2") return (SLIDES[i].media2 || {}).w || 400;
|
||||
if (key === "note") return (SLIDES[i].note || {}).w || 320;
|
||||
if (key === "vis") return SLIDES[i].visW || 900;
|
||||
if (key === "vis") return visMass(SLIDES[i]).visW || 900;
|
||||
return (STANDARD[key] || {}).size;
|
||||
}
|
||||
|
||||
@@ -4996,6 +5031,36 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
Textblock darueber steht dann sichtbar aus der Achse. */
|
||||
let bandTon = "orange", rasterAn = "punkte", textMitte = "an";
|
||||
|
||||
/* ─── Format ──────────────────────────────────────────────────────────────
|
||||
4:5 ist das Format der Beitraege. 1:1 ist das Format der Anzeige: Meta
|
||||
nimmt ein Carousel, in dem ein Video steckt, im Instagram-Feed nur
|
||||
quadratisch. Wortlaut im Ads Guide (abgerufen 02.11.2026): „Derzeit wird
|
||||
bei Carousels, die Videos enthalten, nur das Format 1:1 unterstützt.“
|
||||
Reine Bild-Carousels duerften 4:5 bleiben.
|
||||
|
||||
Die Breite bleibt 1080, nur die Hoehe wechselt. Texte, Slides und
|
||||
Verschiebungen teilen sich beide Formate; was sich je Format
|
||||
unterscheidet, steht an der Slide unter `quadrat` (Groesse und Lage der
|
||||
Karte). Die URL kann das Format vorgeben (?format=1x1), damit
|
||||
animation.mjs jedes Format bauen kann, ohne den Stand umzustellen. */
|
||||
const FORMATE = {
|
||||
"4x5": { h: 1350, name: "4:5", datei: "" },
|
||||
"1x1": { h: 1080, name: "1:1", datei: "-1x1" },
|
||||
};
|
||||
let format = "4x5";
|
||||
let SLIDE_H = FORMATE[format].h;
|
||||
const FORMAT_URL = (f => FORMATE[f] ? f : null)(new URLSearchParams(location.search).get("format"));
|
||||
function formatAnwenden() {
|
||||
if (!FORMATE[format]) format = "4x5";
|
||||
SLIDE_H = FORMATE[format].h;
|
||||
document.documentElement.style.setProperty("--slide-h", SLIDE_H + "px");
|
||||
document.documentElement.dataset.format = format;
|
||||
}
|
||||
/* Die Karte je Format. Fuer 4:5 gilt, was an der Slide steht; fuer 1:1
|
||||
ueberschreibt `quadrat` Breite, Abstand links und Abstand unten. */
|
||||
const visMass = sl => (format === "1x1" && sl.quadrat) ? Object.assign({}, sl, sl.quadrat) : sl;
|
||||
window.SLIDE_FORMAT = () => ({ format, breite: 1080, hoehe: SLIDE_H, datei: FORMATE[format].datei });
|
||||
|
||||
/* ─── Projektstand ────────────────────────────────────────────────────────
|
||||
Alles, was am Projekt hängt, wird gespeichert: Einstellungen, Layouts,
|
||||
Muster und die Verschiebungen im Canvas. Das Werkzeug soll den Stand
|
||||
@@ -5021,7 +5086,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
function currentState() {
|
||||
return {
|
||||
v: STATE_V, variant, tilt, superSample, view, tagOut, frameStyle, bandTon,
|
||||
rasterAn, textMitte,
|
||||
rasterAn, textMitte, format,
|
||||
current, layoutState, bgState, edits,
|
||||
// Welche Slides der Post ueberhaupt hat. Ohne das waere eine selbst
|
||||
// angelegte Slide nach dem Neuladen weg.
|
||||
@@ -5169,6 +5234,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
press("rasters", "r", rasterAn);
|
||||
press("mitten", "m", textMitte);
|
||||
press("views", "w", view);
|
||||
press("formate", "g", format);
|
||||
// Nach einem Schritt, der Slides hinzugefuegt, geloescht oder umsortiert
|
||||
// hat, stimmt der Aufbau im Canvas nicht mehr mit dem Stand ueberein.
|
||||
if (deck.querySelectorAll(".holder").length !== SLIDES.length) {
|
||||
@@ -5180,6 +5246,8 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
applyEditsAll();
|
||||
}
|
||||
anzahlNachziehen();
|
||||
animLeisteBauen();
|
||||
fitStrip();
|
||||
show(current);
|
||||
if (picked) pick(picked.slide, picked.key); else syncEditPanel();
|
||||
} finally { restoring = false; }
|
||||
@@ -5305,6 +5373,8 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
bandTon = d.bandTon || bandTon;
|
||||
rasterAn = d.rasterAn || rasterAn;
|
||||
textMitte = d.textMitte || textMitte;
|
||||
format = FORMAT_URL || (FORMATE[d.format] ? d.format : format);
|
||||
formatAnwenden();
|
||||
current = Number.isInteger(d.current) ? Math.min(d.current, SLIDES.length - 1) : 0;
|
||||
}
|
||||
|
||||
@@ -5350,7 +5420,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
const scroller = document.getElementById("scroller");
|
||||
const availW = Math.max(320, scroller.clientWidth - 120);
|
||||
const availH = Math.max(240, scroller.clientHeight - 120);
|
||||
return Math.min(availW / stripWidth(), availH / 1350, 0.45);
|
||||
return Math.min(availW / stripWidth(), availH / SLIDE_H, 0.45);
|
||||
}
|
||||
|
||||
/* Die Uebersicht muss wissen, wie breit dieser Streifen ist -- die Zahl
|
||||
@@ -5359,7 +5429,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
Interna der Post-Seite unabhaengig. */
|
||||
function groesseMelden() {
|
||||
if (!NUR_STREIFEN || parent === window) return;
|
||||
parent.postMessage({ hg: "streifen", w: stripWidth(), h: 1350,
|
||||
parent.postMessage({ hg: "streifen", w: stripWidth(), h: SLIDE_H,
|
||||
titel: document.title }, "*");
|
||||
}
|
||||
|
||||
@@ -5368,7 +5438,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
const k = zoom || autoFit();
|
||||
deck.style.transform = "scale(" + k + ")";
|
||||
wrap.style.width = Math.round(stripWidth() * k) + "px";
|
||||
wrap.style.height = Math.round(1350 * k) + "px";
|
||||
wrap.style.height = Math.round(SLIDE_H * k) + "px";
|
||||
const lvl = document.getElementById("zoom-fit");
|
||||
if (lvl) lvl.textContent = zoom ? Math.round(k * 100) + " %" : "Fit";
|
||||
groesseMelden();
|
||||
@@ -5490,7 +5560,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
rechten Kante der vorletzten Slide -- zwei Pixel innerhalb der Naht,
|
||||
weil genau auf der Kante ein Antialiasing-Splitter auf der
|
||||
Nachbarslide stehen bleibt. */
|
||||
const H = 1350;
|
||||
const H = SLIDE_H;
|
||||
const A0 = BAND_VON * 1080 + 2; // Anker der Kurve
|
||||
const A1 = Math.max(A0, (SLIDES.length - BAND_BIS) * 1080 - 2);
|
||||
const X0 = Math.min(A1, BAND_VON * 1080 + BAND_ANSCHNITT); // ab hier gezeichnet
|
||||
@@ -5684,9 +5754,10 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
an der linken Slide-Kante beginnt und der Pfeil von Slide 4 dort
|
||||
ankommt. Mit || waere die 0 als fehlender Wert durchgegangen und die
|
||||
Flaeche haette weiter bei 92 gesessen. */
|
||||
const m = visMass(sl);
|
||||
v.style.cssText =
|
||||
"--vis-w:" + (sl.visW ?? 900) + "px;--vis-left:" + (sl.visLeft ?? 92) +
|
||||
"px;--vis-bottom:" + (sl.visBottom ?? 96) + "px";
|
||||
"--vis-w:" + (m.visW ?? 900) + "px;--vis-left:" + (m.visLeft ?? 92) +
|
||||
"px;--vis-bottom:" + (m.visBottom ?? 96) + "px";
|
||||
v.innerHTML = VIS[sl.vis](j, animT(j));
|
||||
page.appendChild(v);
|
||||
}
|
||||
@@ -5817,9 +5888,10 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
|
||||
const v = document.createElement("div");
|
||||
v.className = "vis vis-" + a.vis;
|
||||
const m = visMass(a);
|
||||
v.style.cssText =
|
||||
"--vis-w:" + (a.visW ?? 900) + "px;--vis-left:" + (a.visLeft ?? 92) +
|
||||
"px;--vis-bottom:" + (a.visBottom ?? 96) + "px";
|
||||
"--vis-w:" + (m.visW ?? 900) + "px;--vis-left:" + (m.visLeft ?? 92) +
|
||||
"px;--vis-bottom:" + (m.visBottom ?? 96) + "px";
|
||||
v.innerHTML = VIS[a.vis](i, animT(i));
|
||||
page.appendChild(v);
|
||||
|
||||
@@ -5886,6 +5958,9 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
zeile.textContent = "Chrome startet …";
|
||||
zeile.dataset.ton = "";
|
||||
try {
|
||||
/* animation.mjs liest das Format aus der Datei im Repo. Ein gerade
|
||||
umgeschaltetes Format haengt womoeglich noch im Timer: erst schreiben. */
|
||||
if (pushTimer) { clearTimeout(pushTimer); await pushState(currentState()); }
|
||||
const an = await fetch("/anim", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
@@ -5953,7 +6028,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
kopf.innerHTML = ANIM_PFEIL + "<b>Animation</b><span>" +
|
||||
(liste.length
|
||||
? liste.length + (liste.length === 1 ? " Element" : " Elemente") +
|
||||
" · 1080 × 1350 · 30 fps · MP4, getrennt vom Bildexport"
|
||||
" · 1080 × " + SLIDE_H + " · 30 fps · MP4, getrennt vom Bildexport"
|
||||
: "noch keine in diesem Projekt") + "</span>";
|
||||
leiste.innerHTML = "";
|
||||
leiste.appendChild(kopf);
|
||||
@@ -5984,7 +6059,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
rechts.innerHTML =
|
||||
'<div class="animtitel">' + a.titel + '</div>' +
|
||||
'<div class="animwerte">' + a.dauer.toFixed(1) + " s · " +
|
||||
Math.round(a.dauer * 30) + " Bilder · 1080 × 1350</div>" +
|
||||
Math.round(a.dauer * 30) + " Bilder · 1080 × " + SLIDE_H + "</div>" +
|
||||
'<input class="animschieber" type="range" min="0" max="1000" value="0" ' +
|
||||
'data-animschieber="' + a.i + '" aria-label="Zeitpunkt">' +
|
||||
'<div class="animknoepfe">' +
|
||||
@@ -5992,7 +6067,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
'<button type="button" class="animexport">MP4 exportieren</button>' +
|
||||
"</div>" +
|
||||
'<p class="animzeile"><code>node animation.mjs ' + PROJECT + ".html " +
|
||||
a.key + "</code></p>";
|
||||
a.key + (format === "4x5" ? "" : " " + format) + "</code></p>";
|
||||
karte.appendChild(rechts);
|
||||
reihe.appendChild(karte);
|
||||
|
||||
@@ -6256,12 +6331,12 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
const k = superSample;
|
||||
return {
|
||||
width: 1080 * k,
|
||||
height: 1350 * k,
|
||||
height: SLIDE_H * k,
|
||||
pixelRatio: 1,
|
||||
fontEmbedCSS: fontEmbedCss,
|
||||
style: {
|
||||
width: "1080px",
|
||||
height: "1350px",
|
||||
height: SLIDE_H + "px",
|
||||
transform: k === 1 ? "none" : "scale(" + k + ")",
|
||||
transformOrigin: "top left",
|
||||
},
|
||||
@@ -6283,7 +6358,8 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
}
|
||||
|
||||
// Numeric prefix keeps the upload order intact in the file picker.
|
||||
const fileName = i => String(i + 1).padStart(2, "0") + "-" + SLIDES[i].key + "-" + variant + ".png";
|
||||
const fileName = i => String(i + 1).padStart(2, "0") + "-" + SLIDES[i].key + "-" + variant +
|
||||
FORMATE[format].datei + ".png";
|
||||
|
||||
/* html-to-image serialises the slide into a data:image/svg+xml URL and paints
|
||||
that as a single image. Raster children of a foreignObject decode
|
||||
@@ -6652,7 +6728,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
|
||||
// Die Ausgabegröße hängt an der Renderqualität und wird mitgesagt, damit
|
||||
// sichtbar ist, was tatsächlich auf der Platte landet.
|
||||
const ausgabeGroesse = () => (1080 * superSample) + " × " + (1350 * superSample) + " px";
|
||||
const ausgabeGroesse = () => (1080 * superSample) + " × " + (SLIDE_H * superSample) + " px";
|
||||
|
||||
async function downloadOne() {
|
||||
setStatus("Rendere Slide " + (current + 1) + " …");
|
||||
@@ -6715,7 +6791,8 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
const n = SLIDES.length;
|
||||
const kopf = document.getElementById("kopfzeile");
|
||||
if (kopf) kopf.textContent =
|
||||
(n === 1 ? "Einzelbild" : "Carousel, " + n + " Slides") + " · 1080 × 1350 px";
|
||||
(n === 1 ? "Einzelbild" : "Carousel, " + n + " Slides") + " · 1080 × " + SLIDE_H +
|
||||
" px · " + FORMATE[format].name;
|
||||
const zip = document.getElementById("dl-all");
|
||||
if (zip) {
|
||||
zip.textContent = "Alle " + n + " Slides als ZIP";
|
||||
@@ -7264,8 +7341,8 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
}
|
||||
if (senk) {
|
||||
const ziel = senk === "oben" ? RAND
|
||||
: senk === "mitte" ? (1350 - h) / 2
|
||||
: 1350 - RAND - h;
|
||||
: senk === "mitte" ? (SLIDE_H - h) / 2
|
||||
: SLIDE_H - RAND - h;
|
||||
set.dy = Math.round((set.dy || 0) + (ziel - o));
|
||||
}
|
||||
applyEditsAll();
|
||||
@@ -7740,7 +7817,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
html += "</table>";
|
||||
}
|
||||
html += '<h4>Fest</h4><table>' +
|
||||
'<tr><td>Slide</td><td>1080 × 1350</td></tr>' +
|
||||
'<tr><td>Slide</td><td>1080 × 1350 (4:5) oder 1080 × 1080 (1:1)</td></tr>' +
|
||||
'<tr><td>Innenabstand</td><td>92 px</td></tr>' +
|
||||
'<tr><td>Tag ausgerückt</td><td>−20 px</td></tr>' +
|
||||
'<tr><td>Mockup</td><td>1090 px · 16:10</td></tr>' +
|
||||
@@ -7882,6 +7959,25 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
if (b) { tilt = b.dataset.t; press("tilts", "t", tilt); applyAll(); saveState(); }
|
||||
});
|
||||
|
||||
/* Formatwechsel: Hoehe setzen, alle Slides und das Bewegtbild neu bauen.
|
||||
Die Karten bekommen dabei ihre Masse fuer das Format (visMass). */
|
||||
document.getElementById("formate").addEventListener("click", e => {
|
||||
const b = e.target.closest("button");
|
||||
if (!b || !FORMATE[b.dataset.g] || b.dataset.g === format) return;
|
||||
format = b.dataset.g;
|
||||
formatAnwenden();
|
||||
press("formate", "g", format);
|
||||
picked = null;
|
||||
deckNeuBauen();
|
||||
anzahlNachziehen();
|
||||
animLeisteBauen();
|
||||
zoom = null;
|
||||
show(current);
|
||||
syncEditPanel();
|
||||
saveState();
|
||||
setStatus("Format " + FORMATE[format].name + " · 1080 × " + SLIDE_H + " px", "ok");
|
||||
});
|
||||
|
||||
document.getElementById("quality").addEventListener("click", e => {
|
||||
const b = e.target.closest("button");
|
||||
if (b) { superSample = Number(b.dataset.q); press("quality", "q", b.dataset.q); saveState(); }
|
||||
@@ -7958,6 +8054,11 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
await bilderNachladen();
|
||||
await preloadAssets();
|
||||
const restored = await loadState();
|
||||
format = FORMAT_URL || format;
|
||||
formatAnwenden();
|
||||
/* Der Rendermodus fuer animation.mjs darf den Stand nicht anfassen:
|
||||
Mit ?format=1x1 wuerde er sonst das Format in die Datei schreiben. */
|
||||
if (document.documentElement.dataset.nur === "anim") stateLocked = true;
|
||||
/* Verweise auf Bilder, die es nicht mehr gibt, hier aufloesen statt sie
|
||||
bis in den Export zu schleppen. Sie entstehen, wenn eine importierte
|
||||
Datei geloescht oder nicht mitgepusht wurde. */
|
||||
@@ -7982,6 +8083,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
press("tagouts", "o", tagOut);
|
||||
press("frames", "f", frameStyle);
|
||||
press("views", "w", view);
|
||||
press("formate", "g", format);
|
||||
anzahlNachziehen();
|
||||
show(current);
|
||||
animLeisteBauen();
|
||||
@@ -7993,7 +8095,7 @@ if (location.search.indexOf("nur=anim") >= 0)
|
||||
fontEmbedCss = await htmlToImage.getFontEmbedCSS(deck.querySelector(".slide"));
|
||||
["dl-png", "dl-all"].forEach(id => document.getElementById(id).disabled = false);
|
||||
if (checkCollisions()) setStatus("Bereit. " + SLIDES.length +
|
||||
(SLIDES.length === 1 ? " Slide" : " Slides") + ", 1080 × 1350 px", "ok");
|
||||
(SLIDES.length === 1 ? " Slide" : " Slides") + ", 1080 × " + SLIDE_H + " px", "ok");
|
||||
/* Ganz zum Schluss: der Rendermodus raeumt die Seite leer, und alles,
|
||||
was danach noch ein Element sucht, faende keines mehr. Ein Aufruf
|
||||
weiter oben hat genau das ausgeloest -- "Cannot set properties of
|
||||
|
||||
@@ -482,6 +482,16 @@ behalten die Originalauflösung, alles außerhalb wird beschnitten.
|
||||
im Grid fehlen links und rechts je ~33 px. Für die drei angepinnten Cover
|
||||
relevant, im Feed nicht
|
||||
|
||||
**1080 × 1080 px (1:1) für Anzeigen mit Video im Carousel.** Ein normaler
|
||||
Beitrag darf ein Video im 4:5-Carousel haben. Als Anzeige nicht: Meta schreibt
|
||||
im Ads Guide für den Instagram-Feed „Derzeit wird bei Carousels, die Videos
|
||||
enthalten, nur das Format 1:1 unterstützt.“ Reine Bild-Carousels dürfen 4:5
|
||||
bleiben. In Reels laufen nur Bild-Carousels (9:16). Abgerufen 02.11.2026.
|
||||
Post 9 hat dafür den Schalter „Format“ (4:5 · Beitrag, 1:1 · Anzeige), die
|
||||
Karten tragen ihre 1:1-Maße unter `quadrat`, und
|
||||
`node animation.mjs post9.html animWebinar 1x1` baut das Video in 1:1
|
||||
(Datei mit `-1x1` im Namen).
|
||||
|
||||
### Layout-Template: Tag, Überschrift, Beschreibung, Bild
|
||||
|
||||
Jede Feature-Slide besteht aus denselben vier Bausteinen in derselben Reihenfolge.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"edits": [
|
||||
{}
|
||||
],
|
||||
"format": "1x1",
|
||||
"frameStyle": "chamfer",
|
||||
"layoutState": [
|
||||
"standard"
|
||||
|
||||
+11
-2
@@ -112,7 +112,12 @@
|
||||
<button type="button" data-gehe="./post6.html">Post 6 · Team-Chat</button>
|
||||
<button type="button" data-gehe="./post7.html">Post 7 · Form Builder</button>
|
||||
<button type="button" data-gehe="./post8.html">Post 8 · E-Mail-Marketing</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Webinar</button>
|
||||
<button type="button" data-gehe="./post9.html">Post 9 · Variante 1 · Webinar-Fenster</button>
|
||||
<button type="button" data-gehe="./post9-v2.html">Post 9 · Variante 2 · Kundengeschichte</button>
|
||||
<button type="button" data-gehe="./post9-v3.html">Post 9 · Variante 3 · Kundengeschichte dunkel</button>
|
||||
<button type="button" data-gehe="./post9-v4.html">Post 9 · Variante 4 · Zahlen</button>
|
||||
<button type="button" data-gehe="./post9-v5.html">Post 9 · Variante 5 · Zahlen dunkel</button>
|
||||
<button type="button" data-gehe="./post9-varianten.html">Post 9 · Varianten im Vergleich</button>
|
||||
<hr>
|
||||
<button type="button" data-klick="neu">Streifen neu laden</button>
|
||||
</div>
|
||||
@@ -142,7 +147,11 @@
|
||||
{ datei: "./post6.html", name: "Post 6 · Team-Chat", theme: "modern" },
|
||||
{ datei: "./post7.html", name: "Post 7 · Form Builder", theme: "modern" },
|
||||
{ datei: "./post8.html", name: "Post 8 · E-Mail-Marketing", theme: "modern" },
|
||||
{ datei: "./post9.html", name: "Post 9 · Webinar", theme: "modern" },
|
||||
{ datei: "./post9.html", name: "Post 9 · Variante 1 · Webinar-Fenster", theme: "modern" },
|
||||
{ datei: "./post9-v2.html", name: "Post 9 · Variante 2 · Kundengeschichte", theme: "modern" },
|
||||
{ datei: "./post9-v3.html", name: "Post 9 · Variante 3 · Kundengeschichte dunkel", theme: "modern" },
|
||||
{ datei: "./post9-v4.html", name: "Post 9 · Variante 4 · Zahlen", theme: "modern" },
|
||||
{ datei: "./post9-v5.html", name: "Post 9 · Variante 5 · Zahlen dunkel", theme: "modern" },
|
||||
];
|
||||
|
||||
const flaeche = document.getElementById("flaeche");
|
||||
|
||||
Reference in new issue
Block a user