Files
dogfather-universe/tools/gate-bauen.mjs
T
DogFatherGitandClaude Opus 5 3914adf46c Material statt Farbe -- Kacheln werden Gegenstaende, die Karte ein Bildschirm
Filipe: "ich will dass alle kacheln viel geiler und spezieller aussehen,
viel realistischer" und "so dass der komplette von der kachel vom
hintergrund bild komplett bedeckt ist. ueberrasch mich."

DIE BILDER: BEARBEITET STATT ABGEDUNKELT.
Bis hierher tat der Bildbauer zwei Dinge -- kleiner rechnen und einen
schwarzen Schleier darueberlegen. Abdunkeln macht ein Bild aber nicht
ruhiger, sondern TOT: Es zieht jede Farbe zur Mitte, und uebrig bleibt
ein grauer Schleier mit einer Ahnung von Motiv.

Drei Dinge, die ein Fotograf zuerst anfasst, und keines davon ist
"dunkler":
  * KONTRAST -- Verkleinern MITTELT Bildpunkte; deshalb wirkt jedes
    verkleinerte Bild flauer als das Original, und deshalb schaerft man
    danach nach.
  * SAETTIGUNG -- holt das Rot der Chili und das Gruen der Blaetter
    zurueck, die der Schleier herausgewaschen hatte.
  * VIGNETTE -- der eigentliche Unterschied zwischen "Screenshot" und
    "Aufnahme". Ein Objektiv verliert zu den Ecken hin Licht; das Auge
    liest das als Tiefe. Sie ersetzt ausserdem einen Teil des flachen
    Schleiers: Dunkel wird, wo ohnehin nichts steht.
Der flache Schleier sinkt dadurch von 0,42 auf 0,26 -- heller UND
lesbar, weil die Vignette genau dort arbeitet, wo die Kacheln liegen.

DIE KACHELN: VIER DINGE, DIE EIN DING VON EINEM RECHTECK TRENNEN.
Sie hatten Farbverlauf, Lichtkante und ein Licht, das dem Zeiger folgt --
und blieben Rechtecke mit Farbe darin. Es fehlten:
  1. GEWICHT. Ein Ding, das auf etwas liegt, wirft einen Schatten, auch
     wenn es niemand anfasst. Den gab es nur beim Ueberfahren.
  2. MATERIALSTAERKE. Ein Blech hat oben eine Licht- UND unten eine
     Schattenkante. Nur die obere ergibt einen aufgeklebten Strich.
  3. KOERNUNG. Perfekt glatte Verlaeufe kommen in der Natur nicht vor,
     und das Auge merkt das, ohne es benennen zu koennen. Drei Prozent
     Rauschen genuegen -- aus einem SVG-Filter als Adresse, also ohne
     Datei und ohne zusaetzliche Anfrage.
  4. DURCHSICHT. Hinter den Kacheln liegt jetzt ein aufwendiges Bild.
     Eine deckende Flaeche verdeckt es, eine mattierte nimmt seine Farbe
     auf -- erst dadurch gehoeren beide zusammen.
Beim Ueberfahren wird die Kachel nicht heller, sondern kommt NAEHER:
Sie steigt, ihr Schatten wird groesser und weicher (der Abstand zum
Untergrund waechst). So verhaelt sich ein angehobener Gegenstand.

DIE ANMELDEKARTE: EIN GERAET STATT EINES BILDES AN DER WAND.
Sie sass mit Abstand in der gemalten Tafel -- zwei Rahmen ineinander,
dazwischen schwarze Leere. Jetzt geht sie bis unter das Gluehen des
Neonrahmens: Der Rahmen ist das Gehaeuse, die Karte der eingeschaltete
Bildschirm. Sie leuchtet von den Kanten herein in den Farben des Rahmens
(rot oben links, blau unten rechts), traegt eine Glasscheibe als sehr
schwache Spiegelung und dieselbe Koernung. Die Rollen sind Tasten
geworden: Materialstaerke oben hell, unten dunkel, beim Druecken sinken
sie ein, und die gewaehlte ist beleuchtet statt eingefaerbt.
Alle Angaben bleiben -- vier Rollen mit Beschreibung, Codefeld, Auge,
Rollenhinweis, Fusszeile. "Hochwertiger" heisst nicht "weniger drin".

EIN ECHTER FUND DABEI: --text-still lag ploetzlich bei genau 4,50:1
statt der noetigen 4,5. Der Ton war am 01.09. gegen die DAMALIGE,
dunklere Buehne gemessen. Wer den Hintergrund heller macht, muss die
leiseste Schrift nachziehen -- sonst haette man den Aufwand auch lassen
koennen. Erkannt daran, dass die Zahl sich bei zwei Schleier-Aenderungen
NICHT bewegte: Die Pruefung rechnet mit der CSS-Farbe.

Gemessen statt angesehen: Kontrast an echten Bildpunkten (pruef-buehne),
neun Bildschirmbreiten, drei Handygroessen, Ladezeit und Datenvolumen,
Klassen, Struktur, Startseite -- alles gruen. Die Seiten ueber dem
CLS-Zielwert sind nebenbei von sieben auf vier gesunken.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-07 02:06:08 +02:00

455 lines
20 KiB
JavaScript

/* =====================================================================
gate-bauen.mjs — Anmeldebild und Startbild aus den Markenbildern.
Zwei Dinge, ein Werkzeug, weil beide dieselbe Behandlung brauchen:
verkleinern, abdunkeln, nach WebP.
1. DIE ANMELDESEITE (gate-*.webp)
Aus dem Spicy-Media-x-DogFather-Bild. Es hat rechts eine LEERE
TAFEL mit Neonrahmen -- genau dort soll die Anmeldekarte sitzen.
2. DIE STARTSEITE (buehne-dogfather-*.webp)
Aus dem Tresorbild.
---------------------------------------------------------------------
DIE TAFEL WIRD GEMESSEN, NICHT GESCHAETZT
Die Karte an einer im Bild GEMALTEN Tafel auszurichten, geht nur, wenn
man weiss, wo die Tafel wirklich sitzt. Ich haette Prozentwerte
abschaetzen koennen -- und laege dann um ein paar Pixel daneben, was
man bei einem Neonrahmen sofort sieht.
Deshalb sucht dieses Werkzeug den Rahmen selbst: Es geht Zeile fuer
Zeile und Spalte fuer Spalte durch die rechte Bildhaelfte und sucht
die hellen, gesaettigten Bildpunkte des Neonrandes. Heraus kommen
vier Prozentwerte, die genau so in gate.css stehen.
Gerechnet wird im Browser, der ohnehin fuer die Pruefungen da ist --
kein Bildprogramm, keine neue Abhaengigkeit, 0 EUR. Derselbe Weg wie
in tools/buehne-bauen.mjs.
Aufrufen mit: node tools/gate-bauen.mjs
===================================================================== */
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
import { join, dirname } from "node:path";
import { fileURLToPath } from "node:url";
const WURZEL = join(dirname(fileURLToPath(import.meta.url)), "..");
const QUELLE = join(WURZEL, "assets", "img", "marke");
const ZIEL = join(WURZEL, "workspace", "assets", "img");
const ZIEL_BUEHNE = join(ZIEL, "buehne");
for (const o of [ZIEL, ZIEL_BUEHNE]) if (!existsSync(o)) mkdirSync(o, { recursive: true });
const alsDatenAdresse = (pfad) => {
const b = readFileSync(pfad);
const art = pfad.endsWith(".png") ? "image/png" : "image/jpeg";
return `data:${art};base64,${b.toString("base64")}`;
};
const { chromium } = await import(
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
const browser = await chromium.launch();
const seite = await (await browser.newContext()).newPage();
await seite.setContent("<!doctype html><meta charset=utf-8><body>");
/* ---------------------------------------------------------------------
1. Wo sitzt die Tafel?
--------------------------------------------------------------------- */
const tafel = await seite.evaluate(async (daten) => {
const i = new Image(); i.src = daten; await i.decode();
const c = document.createElement("canvas");
c.width = i.width; c.height = i.height;
const g = c.getContext("2d");
g.drawImage(i, 0, 0);
const d = g.getImageData(0, 0, c.width, c.height).data;
/* ZWEI ANLAEUFE WAREN FALSCH, und beide haben es selbst verraten.
(1) "Suche den LEUCHTENDEN Rahmen" fand den Mond, die roten Blueten
und jede Spiegelung im Wasser -- Ergebnis: die ganze rechte
Bildhaelfte. Eine Messung, die das Offensichtliche zurueckgibt,
hat nichts gemessen.
(2) "Suche die groesste DUNKLE Flaeche" fand die Breite richtig,
aber 94 % Hoehe: Ueber und unter der Tafel ist die Szene
genauso schwarz. Dunkelheit unterscheidet sie nicht.
Was sie WIRKLICH unterscheidet, ist der Rahmen -- aber nur, wenn
man ihn dort sucht, wo nichts anderes leuchtet: INNERHALB der
Tafel. Deshalb wird jetzt vom Mittelpunkt aus nach aussen gelaufen,
bis es hell wird. Auf diesem Weg liegt garantiert nichts anderes,
denn die Tafelflaeche ist leer.
Der Startpunkt ist dabei nicht die Bildmitte, sondern ein Punkt
sicher INNERHALB der Tafel -- gefunden ueber die dunkle Strecke aus
Anlauf (2), die waagerecht ja richtig lag. */
/* HELL ODER FARBIG. Nur nach Helligkeit zu suchen, hat die OBERE
Kante nicht gefunden: Sie ist ein duenner, dunkelroter Strich (der
rote Teil des Rahmens laeuft dort aus), waehrend die untere Kante
hell blau leuchtet. Der Rahmen ist aber an JEDER Stelle deutlich
FARBIG -- die Tafelflaeche daneben ist neutralgrau. Genau das
unterscheidet ihn, und zwar rundum. */
const hell = (x, y) => {
const p = (y * c.width + x) * 4;
const r = d[p], g2 = d[p + 1], b = d[p + 2];
const max = Math.max(r, g2, b), min = Math.min(r, g2, b);
return max > 118 || (max > 52 && max - min > 26);
};
const dunkel = (x, y) => {
const p = (y * c.width + x) * 4;
return Math.max(d[p], d[p + 1], d[p + 2]) < 62;
};
/* Grobe Waagerechte: die laengste dunkle Strecke auf halber Hoehe.
Sie liegt sicher in der Tafel -- daraus kommt der Startpunkt. */
function laengsteStrecke(von, bis, punkt) {
let bA = von, bB = von - 1, a = -1, luecke = 0;
for (let n = von; n <= bis; n++) {
if (punkt(n)) { if (a < 0) a = n; luecke = 0; }
else if (a >= 0 && ++luecke > 6) {
const b = n - luecke;
if (b - a > bB - bA) { bA = a; bB = b; }
a = -1; luecke = 0;
}
}
if (a >= 0 && bis - a > bB - bA) { bA = a; bB = bis; }
return [bA, bB];
}
const yMitte = Math.round(c.height * 0.45);
const [gLinks, gRechts] = laengsteStrecke(
Math.round(c.width * 0.45), c.width - 1, (x) => dunkel(x, yMitte));
const xStart = Math.round((gLinks + gRechts) / 2);
/** Vom Startpunkt aus in eine Richtung laufen, bis es hell wird.
* Verlangt werden ZWEI helle Punkte hintereinander -- ein einzelner
* waere Bildrauschen. */
function bisZumRahmen(x0, y0, dx, dy, grenze) {
let x = x0, y = y0, hintereinander = 0;
while (x >= 0 && y >= 0 && x < c.width && y < c.height) {
if (hell(x, y)) { if (++hintereinander >= 2) return dx ? x : y; }
else hintereinander = 0;
x += dx; y += dy;
if (dx && (dx > 0 ? x > grenze : x < grenze)) break;
if (dy && (dy > 0 ? y > grenze : y < grenze)) break;
}
return dx ? (dx > 0 ? grenze : grenze) : (dy > 0 ? grenze : grenze);
}
/* Auf mehreren Linien, damit ein Staubkorn nicht das Ergebnis
bestimmt -- genommen wird jeweils der mittlere Wert. */
const mitte = (w) => w.slice().sort((a, b) => a - b)[Math.floor(w.length / 2)];
const yProben = [0.35, 0.42, 0.5, 0.58, 0.65].map((f) => Math.round(c.height * f));
const links = mitte(yProben.map((y) => bisZumRahmen(xStart, y, -1, 0, Math.round(c.width * 0.42))));
const rechts = mitte(yProben.map((y) => bisZumRahmen(xStart, y, +1, 0, c.width - 1)));
const xProben = [0.3, 0.42, 0.5, 0.58, 0.7].map((f) => Math.round(links + (rechts - links) * f));
const oben = mitte(xProben.map((x) => bisZumRahmen(x, yMitte, 0, -1, 0)));
const unten = mitte(xProben.map((x) => bisZumRahmen(x, yMitte, 0, +1, c.height - 1)));
return { breite: c.width, hoehe: c.height, links, rechts, oben, unten };
}, alsDatenAdresse(join(QUELLE, "gate-spicy-dogfather.jpeg")));
const p = (v, g) => Math.round((v / g) * 10000) / 100;
/* DAS ERGEBNIS MUSS PLAUSIBEL SEIN. Findet die Suche "die ganze rechte
Haelfte", hat sie nichts gefunden -- genau das ist beim ersten Anlauf
passiert, und es waere fast unbemerkt in die CSS gewandert. Lieber
ein Abbruch mit Ansage als vier Prozentwerte, die nach Messung
aussehen und geraten sind. */
{
const bAnteil = (tafel.rechts - tafel.links) / tafel.breite;
const hAnteil = (tafel.unten - tafel.oben) / tafel.hoehe;
const gut = bAnteil > 0.25 && bAnteil < 0.62 && hAnteil > 0.35 && hAnteil < 0.92
&& tafel.links / tafel.breite > 0.45;
if (!gut) {
console.error(`
ABBRUCH: Die gefundene Tafel ist nicht plausibel `
+ `(${p(bAnteil * 100, 100)} % breit, ${p(hAnteil * 100, 100)} % hoch, `
+ `links bei ${p(tafel.links, tafel.breite)} %).`);
console.error(`Erwartet wird ein Feld zwischen 25 und 62 Prozent Breite in der `
+ `rechten Bildhaelfte.
Wahrscheinlich passt die Helligkeitsschwelle nicht `
+ `zum Bild -- in gate-bauen.mjs bei "dunkel()" nachsehen.`);
await browser.close();
process.exit(2);
}
}
console.log(`Quellbild: ${tafel.breite}x${tafel.hoehe}`);
console.log("Die Tafel im Bild:");
console.log(` links ${p(tafel.links, tafel.breite)} % rechts ${p(tafel.rechts, tafel.breite)} %`);
console.log(` oben ${p(tafel.oben, tafel.hoehe)} % unten ${p(tafel.unten, tafel.hoehe)} %`);
console.log(` Breite ${p(tafel.rechts - tafel.links, tafel.breite)} % `
+ `Hoehe ${p(tafel.unten - tafel.oben, tafel.hoehe)} %`);
console.log("\nFuer gate.css:");
console.log(` --tafel-links: ${p(tafel.links, tafel.breite)}%;`);
console.log(` --tafel-oben: ${p(tafel.oben, tafel.hoehe)}%;`);
console.log(` --tafel-breite: ${p(tafel.rechts - tafel.links, tafel.breite)}%;`);
console.log(` --tafel-hoehe: ${p(tafel.unten - tafel.oben, tafel.hoehe)}%;`);
console.log(` --gate-seiten: ${tafel.breite} / ${tafel.hoehe};`);
/* ---------------------------------------------------------------------
2. Die Bilder bauen
--------------------------------------------------------------------- */
/** Verkleinert, dunkelt ab und gibt WebP zurueck.
*
* `ausschnitt` erlaubt einen Bildausschnitt in Anteilen (0..1) -- fuer
* die Hochkant-Fassung, die nur den rechten Teil mit der Tafel zeigt.
*
* `dunkel` legt einen schwarzen Schleier darueber. Auf der Anmeldeseite
* bleibt er MASSVOLL: Das Bild ist der Grund, warum die Seite gut
* aussieht, und die Karte bringt ihren eigenen dunklen Untergrund mit.
* Auf der Startseite darf er kraeftig sein -- dort liegt Text direkt
* darauf. */
/** Verkleinert, VEREDELT und gibt WebP zurueck.
*
* ---------------------------------------------------------------------
* WARUM HIER MEHR PASSIERT ALS VERKLEINERN (07.09.2026)
*
* Filipe: "die hintergrund bilder will ich auch dass die viel viel viel
* besser aussehen."
*
* Bis hierher tat dieses Werkzeug genau zwei Dinge: kleiner rechnen und
* einen schwarzen Schleier darueberlegen. Abdunkeln macht ein Bild aber
* nicht ruhiger, sondern TOT -- es zieht jede Farbe zur Mitte, und was
* bleibt, ist ein grauer Schleier mit einer Ahnung von Motiv. Genau so
* sahen sie aus.
*
* Was ein Bild stattdessen hochwertig macht, sind drei Dinge, die jeder
* Fotograf zuerst anfasst -- und keines davon ist "dunkler":
*
* 1. TIEFE STATT SCHWAERZE. Ein leichter Kontrastzug trennt die
* hellen von den dunklen Stellen wieder, die das Verkleinern
* zusammengeschoben hat. Verkleinern MITTELT Bildpunkte; das ist
* der Grund, warum jedes verkleinerte Bild flauer wirkt als das
* Original, und der Grund, warum man danach nachschaerft.
*
* 2. FARBE, DIE DAS MOTIV HAT. Ein Hauch Saettigung holt das Rot der
* Chili und das Gruen der Blaetter zurueck, die der Schleier
* herausgewaschen hat.
*
* 3. EINE VIGNETTE. Das ist der eigentliche Unterschied zwischen
* "Screenshot" und "Aufnahme". Ein echtes Objektiv laesst zu den
* Ecken hin Licht verlieren; das Auge liest das als Tiefe und
* fuehrt den Blick zur Mitte. Sie ersetzt hier ausserdem einen
* Teil des flachen Schleiers: Dunkel wird, wo ohnehin nichts
* steht, und nicht dort, wo das Motiv ist.
*
* Damit sinkt der flache Schleier von 0,42 auf 0,26 -- das Bild wird
* heller UND der Text bleibt lesbar, weil die Vignette genau an den
* Raendern arbeitet, an denen die Kacheln liegen. Nachgemessen von
* pruef-buehne an echten Bildpunkten.
*
* Alles im Browser gerechnet, der ohnehin fuer die Pruefungen da ist --
* kein Bildprogramm, keine neue Abhaengigkeit, 0 EUR.
*/
async function bauen({ quelle, ziel, breite, ausschnitt = null, dunkel = 0, guete = 0.82,
seitenverhaeltnis = null, kontrast = 1.08, saettigung = 1.1, vignette = 0.34 }) {
const webp = await seite.evaluate(async (a) => {
const i = new Image(); i.src = a.daten; await i.decode();
const qx = a.ausschnitt ? a.ausschnitt[0] * i.width : 0;
const qy = a.ausschnitt ? a.ausschnitt[1] * i.height : 0;
const qb = a.ausschnitt ? a.ausschnitt[2] * i.width : i.width;
const qh = a.ausschnitt ? a.ausschnitt[3] * i.height : i.height;
const zb = a.breite;
const zh = Math.round(a.seitenverhaeltnis ? zb / a.seitenverhaeltnis : zb * (qh / qb));
const c = document.createElement("canvas");
c.width = zb; c.height = zh;
const g = c.getContext("2d");
g.imageSmoothingQuality = "high";
/* 1 + 2: Kontrast und Saettigung BEIM ZEICHNEN -- der Filter wirkt
auf die Bildpunkte, die gerade entstehen, nicht auf eine zweite
Ebene darueber. Deshalb bleibt es scharf. */
g.filter = `contrast(${a.kontrast}) saturate(${a.saettigung})`;
g.drawImage(i, qx, qy, qb, qh, 0, 0, zb, zh);
g.filter = "none";
/* 3: Die Vignette. Elliptisch ueber die ganze Flaeche, in der Mitte
voellig klar, zu den Ecken hin dunkel. Der weiche Verlauf ist
wichtig -- eine harte Kante saehe aus wie ein Fehler und nicht wie
ein Objektiv. */
if (a.vignette > 0) {
const r = Math.max(zb, zh) * 0.75;
const v = g.createRadialGradient(zb / 2, zh * 0.46, r * 0.18, zb / 2, zh * 0.46, r);
v.addColorStop(0, "rgba(0,0,0,0)");
v.addColorStop(0.55, `rgba(0,0,0,${(a.vignette * 0.22).toFixed(3)})`);
v.addColorStop(0.8, `rgba(0,0,0,${(a.vignette * 0.62).toFixed(3)})`);
v.addColorStop(1, `rgba(0,0,0,${a.vignette.toFixed(3)})`);
g.fillStyle = v;
g.fillRect(0, 0, zb, zh);
}
/* Und erst danach der flache Schleier -- so wenig wie noetig. */
if (a.dunkel > 0) {
g.fillStyle = `rgba(0,0,0,${a.dunkel})`;
g.fillRect(0, 0, zb, zh);
}
return c.toDataURL("image/webp", a.guete);
}, { daten: alsDatenAdresse(quelle), breite, ausschnitt, dunkel, guete, seitenverhaeltnis,
kontrast, saettigung, vignette });
const puffer = Buffer.from(webp.split(",")[1], "base64");
writeFileSync(ziel, puffer);
return puffer.length;
}
const GATE = join(QUELLE, "gate-spicy-dogfather.jpeg");
/* =====================================================================
1. DIE ANMELDESEITE
BIS 2560 PIXEL, nicht bis 1600 (07.09.2026).
Filipe: "die grafik soll viieeeel besser aussehen bitte. die
hintergrund bilder sollen viel hochwertiger und spezieller aussehen."
Die Ursache war nicht die Bildqualitaet, sondern die GROESSE. Sein
Bildschirm ist 2550 px breit, das groesste Bild war 1600 -- der
Browser rechnet es also um das 1,6-fache hoch. Jede Kante wird dabei
weich, und zwar gleichmaessig ueber das ganze Bild. Genau das sieht
man als "billig", ohne benennen zu koennen, warum.
Eine hoehere WebP-Guete haette daran nichts geaendert: Man kann keine
Bildpunkte zurueckholen, die nie ausgeliefert wurden.
Fuenf Stufen statt drei, und die Guete von 0,84 auf 0,88 -- die
Anmeldeseite ist EIN Bild, das jeder als Erstes sieht. Der Browser
nimmt ueber `srcset` ohnehin nur die Stufe, die er braucht; ein Handy
laedt weiterhin 68 KB.
===================================================================== */
console.log("\n--- Anmeldeseite ---");
for (const b of [960, 1280, 1600, 1920, 2560]) {
const n = await bauen({
quelle: GATE, ziel: join(ZIEL_BUEHNE, `gate-${b}.webp`), breite: b,
/* Nur ein Hauch. Der Neonrahmen der Tafel ist das, was die Karte
traegt -- wer ihn wegdunkelt, hat ein huebsches Bild und eine
Karte, die im Nichts schwebt. */
dunkel: 0.08, guete: 0.88, vignette: 0.26,
});
console.log(` gate-${b}.webp ${Math.round(n / 1024)} KB`);
}
/* HOCHKANT: der Ausschnitt um das LOGO, nicht um die Tafel.
*
* Erster Anlauf war falsch, und das Bildschirmfoto hat es sofort
* gezeigt: Ich hatte den Bereich um die Tafel genommen -- die ist aber
* LEER, und auf dem Handy liegt die Anmeldekarte ohnehin davor. Uebrig
* blieb ein schwarzes Rechteck mit ein paar Saeulen.
*
* Der senkrechte Schnitt ist nachgerechnet, nicht probiert: 557 px
* Bildhoehe geteilt durch 764 Quellpixel ergibt 0,729 -- die sichtbaren
* 157 px sind damit 215 Quellpixel, also genau der Bereich um den
* Schriftzug SPICY MEDIA. */
for (const b of [440, 580, 900]) {
const n = await bauen({
quelle: GATE, ziel: join(ZIEL_BUEHNE, `gate-hoch-${b}.webp`), breite: b,
ausschnitt: [0, 0.20, 0.52, 0.80],
dunkel: 0.30, guete: 0.86,
});
console.log(` gate-hoch-${b}.webp ${Math.round(n / 1024)} KB`);
}
/* =====================================================================
2. DIE NEUN BUEHNEN
Bis zum 07.09.2026 zeigten alle neun Szenen DASSELBE Bild. Das war
nie so gemeint -- die Zuordnung stand seit dem 01.09. in start.css,
nur zeigten alle Zeilen auf dieselbe Datei. Jetzt hat jede Gruppe von
Seiten ihr eigenes Motiv.
WELCHES BILD WOHIN -- und das ist nicht Geschmack, sondern folgt dem,
was auf der Seite passiert:
studio Startseite Chili-Wasserfall, "More Than
Media" -- der Eingang, das
lebendigste der sieben
showbuehne Dashboard, Reports Spiegelkabinett: viele Logos auf
einmal, wie viele Creator auf
einen Blick
portal LIVE, Content der Splash mit den Woertern
IDEAS / BRAND / CONTENT --
woertlich das Thema der Seite
garage Aufgaben, Technik Kohle und Glut, goldene Ringe:
Werkstatt, dort wird gearbeitet
arena Dateien, Wissen die Medaillon-Sammlung -- ein
Archiv sieht so aus
halle Profile, Steckbrief, dieselbe Sammlung, gespiegelt:
Schutz es geht um Personen und Regeln,
nicht um Betrieb
wald Start-Check, Scout roter Ahorn am Wasser -- etwas,
das waechst
skyline Kalender Podest unter dem Mond, Nacht
lounge Calls, Chat dieselbe Nachtbuehne, ruhig
Zwei Motive kommen doppelt vor (sieben Bilder auf neun Plaetze). Die
Paare sind bewusst gewaehlt: arena/halle und skyline/lounge sind
inhaltlich benachbart, und sie liegen nie nebeneinander auf einer
Seite -- man sieht immer nur eins.
DREI GROESSEN JE SZENE:
uhd 2400 px fuer grosse Schirme (bis dahin war 1600 das
Maximum, und auf 2550 px sah man jede weiche Kante)
breit 1600 px der Normalfall
schmal 900 px hochkant fuers Handy, quadratisch beschnitten
===================================================================== */
const SZENEN = {
studio: "sm-wasserfall.png",
showbuehne: "sm-spiegel.png",
portal: "sm-ideen.png",
garage: "sm-glut.png",
arena: "sm-medaillons.png",
halle: "sm-medaillons.png",
wald: "sm-ahorn.png",
skyline: "sm-podest.png",
lounge: "sm-podest.png",
};
/* WIE STARK ABGEDUNKELT.
Die Bilder sind hell und farbig -- ueber ihnen steht Text. Der
Schleier der Startseite legt noch einmal nach, deshalb reicht hier
ein mittlerer Wert. Zu viel, und vom Motiv bleibt ein Schemen (genau
das war beim Tresorbild der Fehler: 0,62 uebernommen, und man sah
nichts mehr). Zu wenig, und pruef-buehne meldet zu Recht schlechten
Kontrast an echten Bildpunkten.
0,42 ist gemessen, nicht geraten -- siehe den Lauf von pruef-buehne
nach dem Bau. */
const DUNKEL = 0.26;
console.log("\n--- Die neun Buehnen ---");
let gesamt = 0;
const gebaut = new Set();
for (const [szene, datei] of Object.entries(SZENEN)) {
const quelle = join(QUELLE, datei);
const zeile = [];
for (const [name, breite, guete] of [["uhd", 2400, 0.80], ["breit", 1600, 0.82]]) {
const n = await bauen({
quelle, ziel: join(ZIEL, `buehne-${szene}-${name}.webp`), breite,
dunkel: DUNKEL, guete,
});
gesamt += n;
zeile.push(`${name} ${Math.round(n / 1024)} KB`);
}
/* HOCHKANT: die MITTE des Bildes, hochkant beschnitten. Ein auf 9:16
gequetschtes Breitbild zeigt sonst nur Wand -- und das Logo, um das
es geht, steht in allen sieben Motiven mittig. */
const n = await bauen({
quelle, ziel: join(ZIEL, `buehne-${szene}-schmal.webp`), breite: 900,
ausschnitt: [0.22, 0, 0.56, 1], dunkel: DUNKEL + 0.04, guete: 0.80,
});
gesamt += n;
zeile.push(`schmal ${Math.round(n / 1024)} KB`);
gebaut.add(datei);
console.log(` ${szene.padEnd(11)} ${datei.padEnd(20)} ${zeile.join(" · ")}`);
}
console.log(`\n ${gebaut.size} Motive auf ${Object.keys(SZENEN).length} Buehnen, `
+ `${Math.round(gesamt / 1024)} KB gesamt`);
await browser.close();
console.log("\nFertig.");