Verwaltung: die Kacheln werden zu geschliffenen Glasplatten
Buehne und Zeigerlicht bleiben unveraendert. Diesmal geht es nur um die Kacheln selbst. SIE LIEGEN IM RAUM, NICHT AUF DER SEITE Faehrt der Zeiger darueber, neigt sich die Kachel ihm entgegen -- als wuerde man eine echte Glasplatte kippen. Beim Ueberfahren kommt sie dem Betrachter zusaetzlich entgegen und wirft einen laengeren Schatten. Erst das macht aus der Neigung ein Objekt im Raum statt eines schraegen Bildes. Der Ausschlag liegt bei hoechstens 7 Grad (gemessen 6,7). Alles darueber verzerrt die Schrift sichtbar, und auf diesen Kacheln wird gearbeitet, nicht nur geschaut. DER INHALT STEHT VOR DER FLAECHE Ueberschriften weiter vorn als Fliesstext. Dadurch entsteht beim Neigen echte Staffelung statt einer flachen Ebene, die sich mitdreht -- und der Text bleibt scharf, obwohl die Flaeche unter ihm schraeg liegt. DAZU EIN GLANZSTREIFEN UND UNGLEICHE ECKEN Ein schmales Licht laeuft ueber die Platte und wandert mit der Neigung. Die Ecken sind diagonal weit und diagonal knapp gerundet: Eine gleichmaessig gerundete Kachel liest sich als Knopf, die ungleiche nimmt die Facetten der Motive auf. DIE FALLE, DIE ICH SCHON KANNTE Die Auftauch-Animation der Kacheln nutzte "transform" und haelt ihren Endwert fest -- eine Animation schlaegt jede normale Regel, die Neigung waere also wirkungslos geblieben. Genau dieselbe Falle wie zuvor bei der Buehne, nur eine Ebene tiefer. Die Animation nutzt jetzt "translate" und "scale" als eigene Eigenschaften; "transform" bleibt der Neigung vorbehalten. DREI FEHLER IM TEST, NICHT IN DER SEITE - Der Staffelungstest raeumte die Probekachel leer. Danach fehlten ihr Ueberschrift und Text, und der Neigungstest stuerzte ab, weil er auf ein nicht vorhandenes Element zugriff. Er hat jetzt einen eigenen Behaelter. - Die Winkelrechnung las die "3" aus "matrix3d" als erste Zahl mit und verschob damit jeden Eintrag um eine Stelle. Der Winkel kam als 0,4 Grad heraus statt als 6,7 -- die Pruefung "flach genug" waere also immer gruen gewesen, egal wie stark die Kachel kippt. - Der Schwellwert fuer den senkrechten Ausschlag war zu streng. Eine flache Kachel ist nur gut 100 Punkte hoch; 20 Punkte vom Rand liegen dort schon fast in der Mitte. Geprueft wird jetzt der Vorzeichenwechsel statt eines festen Betrags. Bei prefers-reduced-motion ist alles davon aus: keine Neigung, keine Tiefe, kein Glanz. Ohne feinen Zeiger entfaellt es ebenfalls -- auf einem Telefon gibt es kein Schweben. Geprueft: 260 Pruefungen gruen (Verwaltung 62, Portfolio 15, CRYONOVA 53, Blickfang 13, System 5, Abmelden 16, Abbruch 42, Angebot 54). Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 298 KiB After Width: | Height: | Size: 298 KiB |
@@ -345,7 +345,16 @@ srv.listen(4404, async () => {
|
||||
console.log("\n===== 3e. TAUCHEN DIE KACHELN GESTAFFELT AUF? =====");
|
||||
const staffel = await seite.evaluate(() => {
|
||||
const be = document.querySelector("#vw-bereich");
|
||||
const probe = document.querySelector("#vw-probe");
|
||||
/* EIGENER Behaelter. Frueher wurde hier die Probekachel geleert --
|
||||
danach fehlten ihr Ueberschrift und Text, und der Neigungstest
|
||||
weiter unten stuerzte ab, weil er auf ein nicht vorhandenes
|
||||
Element zugriff. */
|
||||
let probe = document.querySelector("#vw-staffel");
|
||||
if (!probe) {
|
||||
probe = document.createElement("div");
|
||||
probe.id = "vw-staffel";
|
||||
be.appendChild(probe);
|
||||
}
|
||||
probe.innerHTML = "";
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const k = document.createElement("div");
|
||||
@@ -422,6 +431,92 @@ srv.listen(4404, async () => {
|
||||
pruefe("die Kachelkante bricht das Licht in mehrere Toene", toene >= 6,
|
||||
toene + " Farbstufen");
|
||||
|
||||
console.log("\n===== 3g. NEIGEN SICH DIE KACHELN? =====");
|
||||
await bereichAufbauen(seite, "kunden");
|
||||
const platte = seite.locator("#vw-probe .wd-karte").first();
|
||||
const pk = await platte.boundingBox();
|
||||
|
||||
/* Mit steps bewegen, sonst entsteht kein pointermove. */
|
||||
await seite.mouse.move(pk.x + 25, pk.y + 20, { steps: 10 });
|
||||
await seite.waitForTimeout(420);
|
||||
const eckeA = await platte.evaluate((e) => ({
|
||||
nx: parseFloat(e.style.getPropertyValue("--vw-nx")),
|
||||
ny: parseFloat(e.style.getPropertyValue("--vw-ny")),
|
||||
matrix: getComputedStyle(e).transform,
|
||||
}));
|
||||
await seite.mouse.move(pk.x + pk.width - 25, pk.y + pk.height - 20, { steps: 18 });
|
||||
await seite.waitForTimeout(420);
|
||||
const eckeB = await platte.evaluate((e) => ({
|
||||
nx: parseFloat(e.style.getPropertyValue("--vw-nx")),
|
||||
ny: parseFloat(e.style.getPropertyValue("--vw-ny")),
|
||||
matrix: getComputedStyle(e).transform,
|
||||
}));
|
||||
|
||||
/* Die Werte muessen unabhaengig von der Kachelgroesse sein: -1 am
|
||||
linken Rand, +1 am rechten. Sonst kippt eine breite Kachel
|
||||
staerker als eine schmale, obwohl der Zeiger relativ gleich steht. */
|
||||
/* Geprueft wird der VORZEICHENWECHSEL, nicht ein fester Betrag.
|
||||
Eine flache Kachel ist nur gut 100 Punkte hoch -- 20 Punkte vom
|
||||
Rand liegen dort schon fast in der Mitte, der senkrechte Wert
|
||||
bleibt also klein. Ein Schwellwert von 0,5 haette nur gemessen,
|
||||
wie hoch die Testkachel zufaellig ist. */
|
||||
pruefe("die Neigung kippt in die Gegenrichtung",
|
||||
eckeA.nx < -0.4 && eckeB.nx > 0.4 &&
|
||||
eckeA.ny < 0 && eckeB.ny > 0,
|
||||
`${eckeA.nx}/${eckeA.ny} -> ${eckeB.nx}/${eckeB.ny}`);
|
||||
pruefe("und wird als echte Raumdrehung umgesetzt",
|
||||
eckeA.matrix.startsWith("matrix3d") && eckeA.matrix !== eckeB.matrix,
|
||||
eckeA.matrix.slice(0, 26) + "...");
|
||||
|
||||
/* Der Ausschlag muss klein bleiben, sonst verzerrt die Schrift. Aus
|
||||
der Matrix laesst sich der Winkel ueber den Eintrag [0][2]
|
||||
zurueckrechnen: sin(Drehung um die Hochachse). */
|
||||
/* NUR den Inhalt der Klammern auswerten. Ein Suchmuster ueber die
|
||||
ganze Zeichenkette liest die "3" aus "matrix3d" als erste Zahl mit
|
||||
und verschiebt damit jeden Eintrag um eine Stelle -- der Winkel kam
|
||||
dadurch als 0,4 Grad heraus statt als 7. */
|
||||
const inhalt = eckeB.matrix.slice(eckeB.matrix.indexOf("(") + 1, -1);
|
||||
const zahlen = inhalt.split(",").map((n) => parseFloat(n));
|
||||
const winkel = Math.abs(Math.asin(Math.max(-1, Math.min(1, zahlen[2]))) * 180 / Math.PI);
|
||||
pruefe("aber flach genug, dass die Schrift scharf bleibt", winkel <= 9,
|
||||
winkel.toFixed(1) + " Grad");
|
||||
|
||||
/* Beim Verlassen zuruecksetzen -- sonst bleibt die Kachel schraeg
|
||||
stehen, nachdem der Zeiger laengst weg ist. */
|
||||
await seite.mouse.move(40, 40, { steps: 14 });
|
||||
await seite.waitForTimeout(520);
|
||||
const zurueck = await platte.evaluate((e) => ({
|
||||
nx: e.style.getPropertyValue("--vw-nx"),
|
||||
ny: e.style.getPropertyValue("--vw-ny"),
|
||||
}));
|
||||
pruefe("und sie stellt sich beim Verlassen zurueck",
|
||||
zurueck.nx === "0" && zurueck.ny === "0", `${zurueck.nx}/${zurueck.ny}`);
|
||||
|
||||
const tiefe = await seite.evaluate(() => {
|
||||
const k = document.querySelector("#vw-probe .wd-karte");
|
||||
const h = k.querySelector("h2");
|
||||
const p = k.querySelector("p");
|
||||
return {
|
||||
ueberschrift: getComputedStyle(h).transform,
|
||||
absatz: getComputedStyle(p).transform,
|
||||
ecken: getComputedStyle(k).borderRadius,
|
||||
glanz: getComputedStyle(k, "::before").backgroundImage.slice(0, 30),
|
||||
};
|
||||
});
|
||||
/* Der Inhalt steht vor der Flaeche, Ueberschriften weiter vorn als
|
||||
Fliesstext -- dadurch entsteht beim Neigen echte Staffelung. */
|
||||
pruefe("der Inhalt steht vor der Flaeche",
|
||||
tiefe.ueberschrift !== "none" && tiefe.absatz !== "none" &&
|
||||
tiefe.ueberschrift !== tiefe.absatz,
|
||||
"Ueberschrift und Text auf verschiedenen Ebenen");
|
||||
/* Ungleiche Ecken: Eine gleichmaessig gerundete Kachel liest sich als
|
||||
Knopf, die ungleiche nimmt die Facetten der Motive auf. */
|
||||
const radien = tiefe.ecken.split(/\s+/).map(parseFloat);
|
||||
pruefe("die Ecken sind ungleich geschliffen",
|
||||
new Set(radien).size > 1, tiefe.ecken);
|
||||
pruefe("und ein Glanzstreifen liegt auf der Platte",
|
||||
/gradient/.test(tiefe.glanz), tiefe.glanz);
|
||||
|
||||
console.log("\n===== 4. WECHSELT DER RAUM WIRKLICH? =====");
|
||||
const gesehen = [];
|
||||
for (const bereich of BEREICHE) {
|
||||
@@ -490,6 +585,15 @@ srv.listen(4404, async () => {
|
||||
balken: getComputedStyle(l, "::after").transitionDuration,
|
||||
};
|
||||
});
|
||||
const ruhigNeigung = await ruhig.evaluate(() => {
|
||||
const be = document.querySelector("#vw-bereich");
|
||||
let k = be.querySelector(".wd-karte");
|
||||
if (!k) { k = document.createElement("div"); k.className = "wd-karte"; be.appendChild(k); }
|
||||
return getComputedStyle(k).transform;
|
||||
});
|
||||
pruefe("keine Neigung bei reduzierter Bewegung",
|
||||
ruhigNeigung === "none", ruhigNeigung);
|
||||
|
||||
pruefe("keine Auftauch-Animation der Kacheln",
|
||||
ruhigExtra.kachel === "none", ruhigExtra.kachel);
|
||||
/* Geprueft wird "praktisch sofort", nicht wortwoertlich "0s".
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 277 KiB After Width: | Height: | Size: 278 KiB |
+1
-1
@@ -34,7 +34,7 @@
|
||||
Start alle alten Zwischenspeicher weg. Muss bei jeder Änderung an den
|
||||
Dateien unten hochgezählt werden, sonst hängen Nutzer auf einem alten
|
||||
Stand fest. */
|
||||
const CACHE_NAME = "dogfather-webdesign-v40";
|
||||
const CACHE_NAME = "dogfather-webdesign-v41";
|
||||
|
||||
/* Bausteine, die die Oberfläche zum Anzeigen braucht. Bewusst KEINE
|
||||
HTML-Datei in dieser Liste. */
|
||||
|
||||
+150
-10
@@ -476,9 +476,16 @@
|
||||
Nur die ersten acht bekommen einen Versatz. Bei einer langen Liste
|
||||
wuerde die letzte Kachel sonst spuerbar spaeter kommen -- das
|
||||
fuehlt sich nicht mehr elegant an, sondern langsam. */
|
||||
/* Die Auftauch-Animation nutzt "translate" und "scale" als EIGENE
|
||||
Eigenschaften, nicht "transform".
|
||||
|
||||
Grund: Die raeumliche Neigung weiter unten braucht "transform" fuer
|
||||
sich. Eine Animation haelt ihren Endwert fest und schlaegt jede
|
||||
normale Regel -- die Neigung waere sonst wirkungslos. Dieselbe
|
||||
Falle wie bei der Buehne, nur eine Ebene tiefer. */
|
||||
@keyframes vw-kachel-auf {
|
||||
from { opacity: 0; transform: translateY(14px) scale(.985); }
|
||||
to { opacity: 1; transform: none; }
|
||||
from { opacity: 0; translate: 0 14px; scale: .985; }
|
||||
to { opacity: 1; translate: none; scale: 1; }
|
||||
}
|
||||
#vw-bereich .wd-karte {
|
||||
animation: vw-kachel-auf .5s cubic-bezier(.2,.8,.3,1) both;
|
||||
@@ -553,23 +560,100 @@
|
||||
rgb(var(--vw-farbe) / .12) 42%,
|
||||
transparent 72%);
|
||||
}
|
||||
/* Beim Ueberfahren hebt sich die Kachel minimal an. Sehr wenig --
|
||||
es soll sich anfuehlen, als reagiere sie, nicht als huepfe sie. */
|
||||
}
|
||||
|
||||
/* ---------- DIE KACHEL ALS GESCHLIFFENE GLASPLATTE ----------
|
||||
|
||||
Sie liegt nicht flach auf der Seite, sondern im Raum. Faehrt der
|
||||
Zeiger darueber, neigt sie sich ihm entgegen -- als wuerde man eine
|
||||
echte Glasplatte kippen.
|
||||
|
||||
Der Ausschlag ist bewusst sehr klein (hoechstens 7 Grad). Alles
|
||||
darueber verzerrt die Schrift sichtbar, und auf diesen Kacheln wird
|
||||
gearbeitet, nicht nur geschaut. Genau deshalb kippt auch nur die
|
||||
Kachel, waehrend der Text darin durch translateZ wieder nach vorn
|
||||
geholt wird: Er steht dadurch fast senkrecht zum Auge und bleibt
|
||||
scharf, obwohl die Flaeche unter ihm schraeg liegt. */
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
#vw-bereich {
|
||||
/* Ohne Perspektive gaebe es keine Tiefe -- die Neigung saehe aus
|
||||
wie ein platt gedruecktes Parallelogramm. */
|
||||
perspective: 1400px;
|
||||
}
|
||||
#vw-bereich .wd-karte {
|
||||
transition: transform var(--wd-zeit, .22s) ease, box-shadow var(--wd-zeit, .22s) ease;
|
||||
transform-style: preserve-3d;
|
||||
transform:
|
||||
rotateX(calc(var(--vw-ny, 0) * -7deg))
|
||||
rotateY(calc(var(--vw-nx, 0) * 7deg))
|
||||
translateZ(0);
|
||||
transition:
|
||||
transform .32s cubic-bezier(.2,.8,.3,1),
|
||||
box-shadow .32s ease;
|
||||
will-change: transform;
|
||||
}
|
||||
/* Beim Ueberfahren kommt die Kachel dem Betrachter entgegen und
|
||||
wirft einen laengeren Schatten -- das erst macht aus der Neigung
|
||||
ein Objekt im Raum statt eines schraegen Bildes. */
|
||||
#vw-bereich .wd-karte:hover {
|
||||
transform: translateY(-2px);
|
||||
transform:
|
||||
rotateX(calc(var(--vw-ny, 0) * -7deg))
|
||||
rotateY(calc(var(--vw-nx, 0) * 7deg))
|
||||
translateZ(32px);
|
||||
box-shadow:
|
||||
0 30px 70px -28px rgb(0 0 0 / .9),
|
||||
0 0 40px -14px rgb(var(--vw-farbe) / .40),
|
||||
inset 0 1px 0 rgb(var(--vw-farbe) / .26);
|
||||
0 44px 90px -34px rgb(0 0 0 / .95),
|
||||
0 0 60px -18px rgb(var(--vw-farbe) / .45),
|
||||
inset 0 1px 0 rgb(var(--vw-farbe) / .30);
|
||||
}
|
||||
|
||||
/* Der Inhalt steht vor der Flaeche. Ueberschriften weiter vorn als
|
||||
Fliesstext -- dadurch entsteht beim Neigen echte Staffelung
|
||||
statt einer flachen Ebene, die sich mitdreht. */
|
||||
#vw-bereich .wd-karte > * { transform: translateZ(18px); }
|
||||
#vw-bereich .wd-karte h2,
|
||||
#vw-bereich .wd-karte h3 { transform: translateZ(34px); }
|
||||
|
||||
/* DER GLANZSTREIFEN.
|
||||
|
||||
Ein schmales Licht, das ueber die Platte laeuft und mit der
|
||||
Neigung wandert. Es sitzt im ::before, weil ::after schon den
|
||||
Lichtkegel traegt. */
|
||||
#vw-bereich .wd-karte::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
border-radius: inherit;
|
||||
pointer-events: none;
|
||||
background: linear-gradient(
|
||||
calc(112deg + var(--vw-nx, 0) * 26deg),
|
||||
transparent 34%,
|
||||
rgb(255 255 255 / .07) 46%,
|
||||
rgb(var(--vw-farbe) / .16) 50%,
|
||||
rgb(255 255 255 / .07) 54%,
|
||||
transparent 66%);
|
||||
opacity: 0;
|
||||
transition: opacity .3s ease;
|
||||
}
|
||||
#vw-bereich .wd-karte:hover::before { opacity: 1; }
|
||||
}
|
||||
|
||||
/* Die Ecken sind ungleich gerundet -- diagonal weit, diagonal knapp.
|
||||
Eine gleichmaessig gerundete Kachel liest sich als Knopf; die
|
||||
ungleiche Rundung nimmt die Facetten der Motive auf. */
|
||||
#vw-bereich .wd-karte {
|
||||
border-radius: 36px 6px 36px 6px;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
#vw-bereich .wd-karte { transition: none; }
|
||||
#vw-bereich .wd-karte {
|
||||
transition: none;
|
||||
transform: none;
|
||||
}
|
||||
#vw-bereich .wd-karte:hover { transform: none; }
|
||||
#vw-bereich .wd-karte > *,
|
||||
#vw-bereich .wd-karte h2,
|
||||
#vw-bereich .wd-karte h3 { transform: none; }
|
||||
#vw-bereich .wd-karte::before { display: none; }
|
||||
}
|
||||
|
||||
/* Der Titel steht als einziges frei auf der Buehne. Damit er das
|
||||
@@ -3762,6 +3846,62 @@
|
||||
} catch (e) { /* aeltere Browser: dann eben nur bei resize */ }
|
||||
})();
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
Die Kacheln neigen sich zum Zeiger.
|
||||
|
||||
Das Grundsystem setzt bereits --wd-lichtx/--wd-lichty in
|
||||
Bildpunkten, gemessen ab der Kachelecke. Fuer eine Neigung braucht
|
||||
es aber einen Wert, der von der Kachelgroesse unabhaengig ist:
|
||||
-1 am linken Rand, +1 am rechten. Sonst wuerde eine breite Kachel
|
||||
staerker kippen als eine schmale, obwohl der Zeiger relativ an
|
||||
derselben Stelle steht.
|
||||
|
||||
Beim Verlassen wird auf 0 zurueckgesetzt -- sonst bliebe die
|
||||
Kachel schraeg stehen, nachdem der Zeiger laengst weg ist.
|
||||
--------------------------------------------------------------------- */
|
||||
(function kachelnNeigen() {
|
||||
try {
|
||||
if (!window.matchMedia("(hover: hover) and (pointer: fine)").matches) return;
|
||||
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
|
||||
} catch (e) { return; }
|
||||
|
||||
var offen = false, letzte = null, zx = 0, zy = 0;
|
||||
|
||||
document.addEventListener("pointermove", function (e) {
|
||||
var karte = e.target && e.target.closest
|
||||
? e.target.closest("#vw-bereich .wd-karte") : null;
|
||||
if (!karte) return;
|
||||
letzte = karte; zx = e.clientX; zy = e.clientY;
|
||||
if (offen) return;
|
||||
offen = true;
|
||||
requestAnimationFrame(function () {
|
||||
offen = false;
|
||||
if (!letzte) return;
|
||||
var r = letzte.getBoundingClientRect();
|
||||
if (!r.width || !r.height) return;
|
||||
/* -1 bis +1, Nullpunkt in der Kachelmitte. */
|
||||
var nx = ((zx - r.left) / r.width - 0.5) * 2;
|
||||
var ny = ((zy - r.top) / r.height - 0.5) * 2;
|
||||
letzte.style.setProperty("--vw-nx", nx.toFixed(3));
|
||||
letzte.style.setProperty("--vw-ny", ny.toFixed(3));
|
||||
});
|
||||
}, { passive: true });
|
||||
|
||||
/* pointerout statt mouseleave: Es meldet zuverlaessig auch dann,
|
||||
wenn der Zeiger die Kachel ueber ein Kindelement verlaesst. */
|
||||
document.addEventListener("pointerout", function (e) {
|
||||
var karte = e.target && e.target.closest
|
||||
? e.target.closest("#vw-bereich .wd-karte") : null;
|
||||
if (!karte) return;
|
||||
/* Nur zuruecksetzen, wenn der Zeiger die Kachel WIRKLICH verlaesst
|
||||
und nicht bloss von einem Kind zum naechsten wandert. */
|
||||
if (e.relatedTarget && karte.contains(e.relatedTarget)) return;
|
||||
karte.style.setProperty("--vw-nx", "0");
|
||||
karte.style.setProperty("--vw-ny", "0");
|
||||
if (letzte === karte) letzte = null;
|
||||
}, { passive: true });
|
||||
})();
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
Das Licht folgt dem Zeiger auch auf den Reitern.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user