Files
dogfather-universe/pruef-bewegung.mjs
T
DogFatherGitandClaude Opus 5 81c7d40274 Grosse Kacheln kippen jetzt weniger als kleine
Auf der Portfolio-Seite war die Bewegung zu stark. Der Grund liegt nicht
am Winkel, sondern an der Groesse: Bei gleichem Winkel legt eine grosse
Kachel an ihren Ecken viel mehr Weg zurueck als eine kleine.

Eine Preiskachel auf der Startseite ist 282 Punkte lang, eine
Portfolio-Kachel 1503. Fuenf Grad sehen bei der einen beilaeufig aus und
bei der anderen wie das Kippen des halben Bildschirms -- obwohl in
beiden Faellen exakt derselbe Wert im Stilblatt steht.

Der Winkel haengt jetzt an der Kachelgroesse. Bezugswert sind 420
Punkte: Kacheln bis dahin kippen voll, groessere anteilig weniger. Nach
unten bei 1,4 Grad begrenzt, damit auch die groesste Kachel noch
erkennbar reagiert und der Effekt nicht einfach ausfaellt.

Gemessen ueber alle Seiten:

  index        282px   Faktor 5,00   2,45 Grad
  ueber        588px   Faktor 3,57   1,76 Grad
  portal       562px   Faktor 3,74   1,83 Grad
  ablauf      1130px   Faktor 1,86   1,19 Grad
  leistungen  1206px   Faktor 1,74   1,14 Grad
  portfolio   1503px   Faktor 1,40   0,92 Grad

Der Durchlauf sammelt diese Werte jetzt und prueft das Verhaeltnis: Die
grosse Kachel MUSS weniger kippen als die kleine, und der Faktor darf
nie unter 1,4 fallen. Ein blosser Test auf "hoechstens neun Grad" haette
den Unterschied nicht bemerkt -- beide Faelle lagen ja deutlich
darunter, und trotzdem war einer davon zu viel.

Geprueft: 298 Pruefungen gruen (Bewegung 34, Portfolio 19, Verwaltung 62,
CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42,
Angebot 54).

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-25 13:11:53 +02:00

237 lines
10 KiB
JavaScript

/* =====================================================================
Prueft, dass Kacheln und Bilder auf ALLEN Seiten auf den Zeiger
reagieren -- nicht nur im Verwaltungsbereich.
Warum als eigener Durchlauf: Die Effekte liegen im Grundsystem
(webdesign.css und wd-core.js) und gelten damit fuer jede Seite. Eine
Pruefung, die nur eine Seite ansieht, wuerde nicht auffallen lassen,
wenn eine andere Seite den Effekt still ueberschreibt. Genau das ist
passiert: ".wd-bereit .wd-auf.wd-sichtbar" setzt "transform: none"
und hat mit hoeherer Spezifitaet die Neigung geloescht -- sichtbar
nur auf Seiten, deren Kacheln eine Einblendung tragen.
===================================================================== */
import { chromium } from "playwright";
import http from "http";
import fs from "fs";
import path from "path";
const TYP = {
".html": "text/html", ".css": "text/css", ".js": "text/javascript",
".png": "image/png", ".svg": "image/svg+xml", ".webp": "image/webp",
".json": "application/json", ".ico": "image/x-icon", ".jpg": "image/jpeg",
};
const srv = http.createServer((q, r) => {
const p = path.join(process.cwd(), decodeURIComponent(q.url.split("?")[0]));
if (fs.existsSync(p) && !p.endsWith("/")) {
r.writeHead(200, { "content-type": TYP[path.extname(p)] || "text/plain" });
return r.end(fs.readFileSync(p));
}
r.writeHead(404);
r.end("");
});
let ok = 0, fehl = 0;
const pruefe = (name, gut, info) => {
if (gut) { ok++; console.log(" ok " + name + (info ? " -> " + info : "")); }
else { fehl++; console.log(" FEHL " + name + (info ? " -> " + info : "")); }
};
const SEITEN = ["index", "leistungen", "ablauf", "portfolio", "ueber", "portal"];
srv.listen(4405, async () => {
const browser = await chromium.launch();
const gemessen = [];
console.log("1. NEIGEN SICH DIE KACHELN AUF JEDER SEITE?");
for (const name of SEITEN) {
const seite = await browser.newPage({ viewport: { width: 1440, height: 900 } });
const skriptfehler = [];
seite.on("pageerror", (e) => skriptfehler.push(String(e).slice(0, 110)));
await seite.goto(`http://127.0.0.1:4405/webdesign/${name}.html`);
/* Die Startseite legt beim Laden einen Vorhang ueber alles. Wer zu
frueh misst, trifft den Vorhang statt der Kachel. */
await seite.waitForTimeout(4500);
const karte = seite.locator(".wd-karte").first();
if (await karte.count() === 0) {
pruefe(`${name}: Kachel vorhanden`, false, "keine .wd-karte");
await seite.close();
continue;
}
await karte.scrollIntoViewIfNeeded();
await seite.waitForTimeout(700);
/* Einen Punkt suchen, der WIRKLICH auf der Kachel liegt und im
Fenster sichtbar ist. Eine Portfolio-Kachel ist ueber 1400 Punkte
hoch -- ein fester Anteil ihrer Hoehe landet ausserhalb des
Bildschirms, und dann misst der Test das Nichts. */
const ziel = await seite.evaluate(() => {
const k = document.querySelector(".wd-karte");
const r = k.getBoundingClientRect();
const x = Math.round(r.left + Math.min(r.width * 0.25, 200));
const y = Math.round(Math.max(80, Math.min(r.top + 60, window.innerHeight - 80)));
const e = document.elementFromPoint(x, y);
return { x, y, trifft: !!(e && e.closest(".wd-karte")) };
});
if (!ziel.trifft) {
pruefe(`${name}: Messpunkt liegt auf der Kachel`, false, JSON.stringify(ziel));
await seite.close();
continue;
}
/* Mit steps bewegen, sonst entsteht kein pointermove. */
await seite.mouse.move(ziel.x + 80, ziel.y + 30, { steps: 8 });
await seite.mouse.move(ziel.x, ziel.y, { steps: 14 });
await seite.waitForTimeout(600);
const d = await karte.evaluate((e) => {
const r = e.getBoundingClientRect();
return {
nx: parseFloat(e.style.getPropertyValue("--wd-nx")),
matrix: getComputedStyle(e).transform,
lumen: getComputedStyle(e).getPropertyValue("--wd-lumen").trim(),
kipp: parseFloat(e.style.getPropertyValue("--wd-kipp")),
laengsteSeite: Math.round(Math.max(r.width, r.height)),
};
});
pruefe(`${name}: die Kachel neigt sich`,
d.matrix.startsWith("matrix3d") && !Number.isNaN(d.nx),
`nx ${d.nx}`);
/* Klein halten: Alles ueber neun Grad verzerrt die Schrift. */
if (d.matrix.startsWith("matrix3d")) {
const zahlen = d.matrix.slice(d.matrix.indexOf("(") + 1, -1).split(",").map(Number);
const winkel = Math.abs(Math.asin(Math.max(-1, Math.min(1, zahlen[2]))) * 180 / Math.PI);
pruefe(`${name}: und bleibt flach genug`, winkel <= 9, winkel.toFixed(1) + " Grad");
gemessen.push({ seite: name, flaeche: d.laengsteSeite, faktor: d.kipp, winkel });
}
/* Beim Verlassen zuruecksetzen. */
await seite.mouse.move(5, 5, { steps: 12 });
await seite.waitForTimeout(600);
const zurueck = await karte.evaluate((e) => e.style.getPropertyValue("--wd-nx"));
pruefe(`${name}: stellt sich beim Verlassen zurueck`, zurueck === "0", zurueck);
pruefe(`${name}: kein Skriptfehler`, skriptfehler.length === 0, skriptfehler[0] || "");
await seite.close();
}
console.log("\n1b. GROSSE KACHELN KIPPEN WENIGER ALS KLEINE");
for (const g of gemessen) {
console.log(" " + g.seite.padEnd(11) + " laengste Seite " +
String(g.flaeche).padStart(4) + "px Faktor " + g.faktor +
" " + g.winkel.toFixed(2) + " Grad");
}
/* Bei gleichem Winkel legt eine grosse Kachel an ihren Ecken viel
mehr Weg zurueck als eine kleine. Eine Portfolio-Kachel ist ueber
1400 Punkte hoch -- dort sah derselbe Winkel aus wie das Kippen des
halben Bildschirms. Deshalb skaliert der Faktor mit der Groesse. */
const gross = gemessen.filter((g) => g.flaeche > 900);
const klein = gemessen.filter((g) => g.flaeche < 500);
if (gross.length && klein.length) {
const groessterGross = Math.max(...gross.map((g) => g.winkel));
const kleinsterKlein = Math.min(...klein.map((g) => g.winkel));
pruefe("die grosse Kachel kippt weniger als die kleine",
groessterGross < kleinsterKlein,
groessterGross.toFixed(2) + " Grad gegen " + kleinsterKlein.toFixed(2) + " Grad");
} else {
pruefe("grosse und kleine Kacheln zum Vergleichen gefunden", false,
gross.length + " grosse, " + klein.length + " kleine");
}
/* Nach unten begrenzt, damit der Effekt bei sehr grossen Kacheln
nicht einfach ausfaellt. */
pruefe("aber auch die groesste reagiert noch sichtbar",
gemessen.every((g) => g.faktor >= 1.4),
"kleinster Faktor " + Math.min(...gemessen.map((g) => g.faktor)));
console.log("\n2. DIE EINBLENDUNG DARF DIE NEIGUNG NICHT LOESCHEN");
const s2 = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await s2.goto("http://127.0.0.1:4405/webdesign/leistungen.html");
await s2.waitForTimeout(2500);
const einblendung = await s2.evaluate(() => {
const k = document.querySelector(".wd-auf.wd-sichtbar") ||
document.querySelector(".wd-auf");
if (!k) return null;
const st = getComputedStyle(k);
return { translate: st.translate, transform: st.transform };
});
/* Der Kern: Die Einblendung muss ueber "translate" laufen. Nutzt sie
"transform", loescht ihr hoeher gewichtetes "transform: none" die
Neigung -- und zwar still, ohne Fehlermeldung. */
pruefe("die Einblendung nutzt translate, nicht transform",
einblendung && einblendung.translate !== undefined,
einblendung ? `translate: ${einblendung.translate}` : "kein .wd-auf gefunden");
await s2.close();
console.log("\n2b. DIE FARBIGE OBERKANTE DARF NICHT VERSCHWINDEN");
const s3 = await browser.newPage({ viewport: { width: 1440, height: 900 } });
await s3.goto("http://127.0.0.1:4405/webdesign/index.html");
await s3.waitForTimeout(4500);
const kappen = await s3.evaluate(() =>
[...document.querySelectorAll(".wd-karte--kappe")].slice(0, 8).map((k) => {
const v = getComputedStyle(k, "::before");
return {
lila: k.classList.contains("wd-karte--lila"),
hoehe: parseFloat(v.height),
oben: parseFloat(v.top),
farbig: /gradient/.test(v.backgroundImage),
};
}));
pruefe("es gibt Kacheln mit Oberkante", kappen.length > 0, kappen.length + " Stueck");
/* Das ist der Kern: Ein Element hat nur ZWEI Pseudoelemente. Der
Glanzstreifen lag zuerst ebenfalls im ::before und hat die farbige
Oberkante stillschweigend geloescht -- sichtbar als "bei einer
Kachel Farbe oben, bei den anderen keine". Die Lila-Fassung hat
eine Klasse mehr und damit mehr Gewicht, ihre Farbe blieb; die
blaue verschwand. Deshalb wird hier ausdruecklich BEIDES geprueft. */
pruefe("jede davon hat eine 5 Punkte hohe Kante ganz oben",
kappen.every((k) => k.hoehe === 5 && k.oben === 0),
kappen.map((k) => k.hoehe + "px").join(" "));
pruefe("und jede traegt Farbe", kappen.every((k) => k.farbig),
kappen.filter((k) => k.farbig).length + " von " + kappen.length);
pruefe("blaue UND lila Kacheln sind dabei",
kappen.some((k) => k.lila) && kappen.some((k) => !k.lila),
kappen.filter((k) => k.lila).length + " lila, " +
kappen.filter((k) => !k.lila).length + " blau");
/* Der Glanz muss trotzdem noch da sein -- er ist jetzt die erste von
zwei Hintergrundebenen im ::after. */
const glanz = await s3.evaluate(() => {
const k = document.querySelector(".wd-karte");
return getComputedStyle(k, "::after").backgroundImage;
});
pruefe("der Glanzstreifen liegt jetzt beim Lichtkegel",
/linear-gradient/.test(glanz) && /radial-gradient/.test(glanz),
(glanz.match(/gradient/g) || []).length + " Verlaeufe");
await s3.close();
console.log("\n3. BEI REDUZIERTER BEWEGUNG IST ALLES AUS");
const ruhig = await browser.newPage({
viewport: { width: 1440, height: 900 }, reducedMotion: "reduce" });
await ruhig.goto("http://127.0.0.1:4405/webdesign/leistungen.html");
await ruhig.waitForTimeout(2000);
const still = await ruhig.evaluate(() => {
const k = document.querySelector(".wd-karte");
return {
transform: getComputedStyle(k).transform,
sichtbar: getComputedStyle(k).opacity,
};
});
pruefe("keine Neigung", still.transform === "none", still.transform);
/* Abschalten heisst NICHT verschwinden: Die Kachel muss sichtbar
bleiben, auch wenn die Einblendung nicht laeuft. */
pruefe("die Kachel ist trotzdem sichtbar", parseFloat(still.sichtbar) > 0.9,
still.sichtbar);
await ruhig.close();
console.log(`\n===== ${ok} bestanden, ${fehl} fehlgeschlagen =====`);
await browser.close();
srv.close();
process.exit(fehl ? 1 : 0);
});