Verwaltung wird fluessig: von 4,5 auf 60 Bilder pro Sekunde

Bei der Systemabnahme gemessen und fuer untragbar befunden: Die
Verwaltung lief mit 4,5 Bildern je Sekunde, sobald die Maus bewegt
wurde. Die Startseite lag bei 60. Ein Arbeitswerkzeug, das bei jeder
Mausbewegung ruckelt, ist kein Gewinn an Schoenheit.

WAS GEMESSEN WURDE, STATT GERATEN

Jeder Effekt einzeln abgeschaltet, mit echter Mausbewegung:

  alles an                6,4 Bilder/s
  ohne Glas               9,7
  ohne Lampe              9,2
  ohne Kachel-Neigung     6,4   (kostet NICHTS)
  ohne Glas UND Lampe    16,4

Kein einzelner Schuldiger -- es war die Wechselwirkung. Die Lampe
verschiebt den Untergrund, woraufhin JEDE Glasflaeche darueber ihre
Rueckseiten-Unschaerfe neu berechnen muss. Beide zusammen kosteten mehr
als beide einzeln.

Die Neigung ist gratis: Sie laeuft auf der Grafikkarte.

Ein zweiter Posten kam dazu: Solange die Kacheln durchscheinend sind,
muss das bildschirmfuellende Buehnenbild bei jeder Neuzeichnung
mitgerechnet werden. Ohne Buehnenbild stieg die Rate von 24 auf 34,6.

DREI EINGRIFFE

1. Glas nur noch auf dem Detailblatt. Davon gibt es immer genau eines,
   und es liegt gross ueber der Seite -- dort faellt die Rechenzeit
   einmal an, nicht pro Listeneintrag. Kacheln, Reiter und Knoepfe
   bekommen stattdessen eine dichte Flaeche. Optisch kaum ein
   Unterschied, weil die Struktur des Bildes ohnehin verschwindet.

2. Die Vollbild-Zeigerlampe ist abgeschaltet. Das Licht, das dem Zeiger
   folgt, gibt es weiterhin -- auf den Kacheln selbst. Das ist der
   Effekt, der zaehlt, und er ist billig: Er betrifft nur die Kachel
   unter dem Zeiger statt des ganzen Bildschirms.

3. Die Kachelflaeche ist dichter (.92/.96 statt .52/.68). Die Buehne
   bleibt rings um die Kacheln voll sichtbar, durch die Kachel selbst
   schimmert sie nur noch als Ahnung.

ERGEBNIS

  in Ruhe (lesen)        55,6 -> 60,6 Bilder/s
  beim Scrollen          54,0
  Zeiger bewegt           4,5 ->   28

DIE BILDRATE IST JETZT SELBST EIN PRUEFPUNKT

Ohne ihn kaeme jederzeit ein weiterer huebscher Effekt dazu, der die
Seite still wieder zaeh macht -- und niemand wuesste, welcher es war.
Der Durchlauf verlangt jetzt ueber 45 Bilder je Sekunde in Ruhe.

NEBENBEFUND

Die Regel fuer das Detailblatt stand unter "#vw-bereich" -- das Blatt
liegt aber in einer eigenen Ueberlagerung. Die Regel griff also nie:
Ausgerechnet die eine Flaeche, die eine Unschaerfe wirklich verdient,
hatte als einzige keine. Gefunden hat das der neue Test.

Geprueft: 1281 Pruefungen gruen (Browser 715, Server 566), i18n ohne
Fehler, WCAG ohne Fundstellen.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-08-25 17:34:53 +02:00
co-authored by Claude Opus 5
parent 0ef64ba222
commit bd0346f785
18 changed files with 283 additions and 39 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 KiB

After

Width:  |  Height:  |  Size: 923 KiB

+107
View File
@@ -0,0 +1,107 @@
# -*- coding: utf-8 -*-
import io
p = 'pruef-verwaltung-schmuck.mjs'
s = io.open(p, encoding='utf-8').read()
# --- Glas-Abschnitt: es traegt jetzt nur noch das Detailblatt Unschaerfe ---
alt = ''' console.log("\\n===== 2. TRAEGT ALLES GLAS, WORAUF GELESEN WIRD? =====");
const glas = await seite.evaluate(() => {
const pruefen = [".vw-karte", ".vw-filter-knopf", ".vw-wer"];
const raus = {};
for (const w of pruefen) {
const el = document.querySelector("#vw-bereich " + w);
if (!el) { raus[w] = "(nicht gefunden)"; continue; }
const st = getComputedStyle(el);
raus[w] = st.backdropFilter || st.webkitBackdropFilter || "keins";
}
return raus;
});
for (const [w, wert] of Object.entries(glas)) {
if (wert === "(nicht gefunden)") continue;
pruefe(` ${w} hat Rueckseiten-Unschaerfe`, /blur/.test(wert), wert.slice(0, 34));
}'''
neu = ''' console.log("\\n===== 2. BLEIBT DER TEXT VOM BILD GETRENNT? =====");
/* Frueher wurde hier "traegt alles Glas?" geprueft. Das war zu teuer:
Eine Rueckseiten-Unschaerfe kostet Rechenzeit PRO ELEMENT, und zwar
bei jeder Bewegung neu. Mit sechs Kacheln fiel die Bildrate auf
6,4 Bilder je Sekunde, bei echter Mausbewegung sogar auf 4,5.
Entscheidend ist nicht die Unschaerfe, sondern dass der Text nicht
auf dem Bild steht. Das leistet auch eine dichte Flaeche -- und die
kostet nichts. Genau das wird jetzt geprueft. */
const flaechen = await seite.evaluate(() => {
const el = document.querySelector("#vw-probe .vw-karte");
if (!el) return null;
const st = getComputedStyle(el);
return { hintergrund: st.backgroundImage, unschaerfe: st.backdropFilter };
});
pruefe("die Kachel hat eine eigene Flaeche unter dem Text",
flaechen && /gradient/.test(flaechen.hintergrund),
flaechen ? "Verlauf vorhanden" : "keine Kachel");
/* Das Detailblatt behaelt die Unschaerfe: Davon gibt es immer nur
genau eines, und es liegt gross ueber der Seite -- dort faellt die
Rechenzeit einmal an, nicht pro Listeneintrag. */
const blatt = await seite.evaluate(() => {
const el = document.querySelector(".vw-detail-blatt");
if (!el) return "(nicht auf der Seite)";
return getComputedStyle(el).backdropFilter || "keins";
});
if (blatt !== "(nicht auf der Seite)") {
pruefe("das Detailblatt behaelt seine Unschaerfe", /blur/.test(blatt), blatt.slice(0, 30));
}
/* Die Bildrate ist selbst ein Pruefpunkt. Ohne ihn waere jederzeit
ein weiterer huebscher Effekt dazugekommen, der die Seite still
wieder zaeh macht. */
const bildrate = await seite.evaluate(() => new Promise((fertig) => {
let n = 0;
const start = performance.now();
const zaehl = () => {
n++;
if (performance.now() - start < 1200) requestAnimationFrame(zaehl);
else fertig(n / ((performance.now() - start) / 1000));
};
requestAnimationFrame(zaehl);
}));
pruefe("die Seite laeuft in Ruhe fluessig (ueber 45 Bilder/s)",
bildrate > 45, bildrate.toFixed(1) + " Bilder/s");'''
assert alt in s, 'Glas-Abschnitt nicht gefunden'
s = s.replace(alt, neu, 1)
# --- Lampen-Abschnitt: sie ist bewusst abgeschaltet ---
alt2 = ''' pruefe("Buehne und Lampe stecken in einem isolierten Raum",
extra.raumIsoliert === "isolate", extra.raumIsoliert);'''
neu2 = ''' pruefe("Buehne und Lampe stecken in einem isolierten Raum",
extra.raumIsoliert === "isolate", extra.raumIsoliert);
/* Die Vollbild-Zeigerlampe ist ABGESCHALTET -- eine Entscheidung,
kein Versehen. Zusammen mit den Glasflaechen kostete sie zwei
Drittel der Bildrate: Jede ihrer Bewegungen zwang jede Flaeche
darueber zum Neurechnen.
Das Licht, das dem Zeiger folgt, gibt es weiterhin auf den Kacheln
selbst. Es ist billig, weil es nur die Kachel unter dem Zeiger
betrifft statt des ganzen Bildschirms. */
const lampeAus = await seite.evaluate(() =>
getComputedStyle(document.querySelector(".vw-lampe")).display);
pruefe("die Vollbild-Lampe ist bewusst aus", lampeAus === "none", lampeAus);'''
assert alt2 in s, 'Lampen-Abschnitt nicht gefunden'
s = s.replace(alt2, neu2, 1)
# --- Die alten Lampen-Pruefungen entfernen ---
for weg in [
''' pruefe("die Lampe beleuchtet, statt zu ueberdecken",
extra.mischung === "soft-light", extra.mischung);''',
''' pruefe("sie folgt dem Zeiger", extra.mx === "1050px" && extra.my === "380px",
extra.mx + " / " + extra.my);''',
''' pruefe("und war vor der ersten Zeigerbewegung aus", extra.lampeAn === true);''',
]:
if weg in s:
s = s.replace(weg, '', 1)
io.open(p, 'w', encoding='utf-8', newline='').write(s)
print('Test an die neue Aufteilung angepasst')
Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 KiB

After

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 793 KiB

After

Width:  |  Height:  |  Size: 793 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 320 KiB

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 925 KiB

After

Width:  |  Height:  |  Size: 801 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 213 KiB

After

Width:  |  Height:  |  Size: 185 KiB

+56 -19
View File
@@ -186,23 +186,50 @@ srv.listen(4404, async () => {
pruefe("sie ist als Zierde ausgezeichnet", buehne.versteckt === "true");
}
console.log("\n===== 2. TRAEGT ALLES GLAS, WORAUF GELESEN WIRD? =====");
const glas = await seite.evaluate(() => {
const pruefen = [".wd-karte", ".vw-filter-knopf", ".vw-wer"];
const raus = {};
for (const w of pruefen) {
const el = document.querySelector("#vw-bereich " + w);
if (!el) { raus[w] = "(nicht gefunden)"; continue; }
const st = getComputedStyle(el);
raus[w] = st.backdropFilter || st.webkitBackdropFilter || "keins";
}
return raus;
console.log("\n===== 2. BLEIBT DER TEXT VOM BILD GETRENNT? =====");
/* Frueher stand hier "traegt alles Glas?". Das war zu teuer: Eine
Rueckseiten-Unschaerfe kostet Rechenzeit PRO ELEMENT, und zwar bei
jeder Bewegung neu. Mit sechs Kacheln fiel die Bildrate auf 6,4
Bilder je Sekunde, bei echter Mausbewegung sogar auf 4,5.
Entscheidend ist nicht die Unschaerfe, sondern dass Text nicht auf
dem Bild steht. Das leistet auch eine dichte Flaeche -- und die
kostet nichts. */
const flaeche = await seite.evaluate(() => {
const el = document.querySelector("#vw-probe .vw-karte");
if (!el) return null;
return getComputedStyle(el).backgroundImage;
});
for (const [w, wert] of Object.entries(glas)) {
if (wert === "(nicht gefunden)") continue;
pruefe(` ${w} hat Rueckseiten-Unschaerfe`, /blur/.test(wert), wert.slice(0, 34));
pruefe("die Kachel hat eine eigene Flaeche unter dem Text",
flaeche && /gradient/.test(flaeche), flaeche ? "Verlauf vorhanden" : "keine Kachel");
/* Das Detailblatt behaelt die Unschaerfe: Davon gibt es immer nur
genau eines, und es liegt gross ueber der Seite -- dort faellt die
Rechenzeit einmal an, nicht pro Listeneintrag. */
const blatt = await seite.evaluate(() => {
const el = document.querySelector(".vw-detail-blatt");
return el ? (getComputedStyle(el).backdropFilter || "keins") : "(nicht auf der Seite)";
});
if (blatt !== "(nicht auf der Seite)") {
pruefe("das Detailblatt behaelt seine Unschaerfe", /blur/.test(blatt), blatt.slice(0, 30));
}
/* Die Bildrate ist selbst ein Pruefpunkt. Ohne ihn kaeme jederzeit
ein weiterer huebscher Effekt dazu, der die Seite still wieder zaeh
macht -- und niemand wuesste, welcher es war. */
const bildrate = await seite.evaluate(() => new Promise((fertig) => {
let n = 0;
const start = performance.now();
const zaehl = () => {
n++;
if (performance.now() - start < 1200) requestAnimationFrame(zaehl);
else fertig(n / ((performance.now() - start) / 1000));
};
requestAnimationFrame(zaehl);
}));
pruefe("die Seite laeuft in Ruhe fluessig (ueber 45 Bilder/s)",
bildrate > 45, bildrate.toFixed(1) + " Bilder/s");
console.log("\n===== 3. BLEIBT DER TEXT LESBAR? =====");
for (const bereich of BEREICHE) {
await bereichAufbauen(seite, bereich);
@@ -427,17 +454,27 @@ srv.listen(4404, async () => {
mitten auf einer Karte. */
pruefe("Buehne und Lampe stecken in einem isolierten Raum",
extra.raumIsoliert === "isolate", extra.raumIsoliert);
/* Die Vollbild-Zeigerlampe ist ABGESCHALTET -- eine Entscheidung,
kein Versehen. Zusammen mit den Glasflaechen kostete sie zwei
Drittel der Bildrate: Jede ihrer Bewegungen zwang jede Flaeche
darueber zum Neurechnen.
Das Licht, das dem Zeiger folgt, gibt es weiterhin auf den Kacheln
selbst. Es ist billig, weil es nur die Kachel unter dem Zeiger
betrifft statt des ganzen Bildschirms. */
const lampeAus = await seite.evaluate(() =>
getComputedStyle(document.querySelector(".vw-lampe")).display);
pruefe("die Vollbild-Lampe ist bewusst aus", lampeAus === "none", lampeAus);
/* soft-light rechnet mit dem, was darunter liegt: dunkle Stellen
bleiben dunkel, vorhandene Lichtkanten werden verstaerkt. "screen"
oder "overlay" wuerden die Eiskanten ausbrennen lassen. */
pruefe("die Lampe beleuchtet, statt zu ueberdecken",
extra.mischung === "soft-light", extra.mischung);
pruefe("sie folgt dem Zeiger", extra.mx === "1050px" && extra.my === "380px",
extra.mx + " / " + extra.my);
/* Erst zeigen, wenn der Zeiger wirklich da war -- sonst haengt beim
Laden ein Lichtfleck in der Bildmitte, den niemand dorthin gelegt
hat. */
pruefe("und war vor der ersten Zeigerbewegung aus", extra.lampeAn === true);
pruefe("es schweben Partikel im Raum", extra.flockenZahl === 9,
extra.flockenZahl + " Stueck");
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 720 KiB

After

Width:  |  Height:  |  Size: 761 KiB

+1 -1
View File
@@ -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-v49";
const CACHE_NAME = "dogfather-webdesign-v50";
/* Bausteine, die die Oberfläche zum Anzeigen braucht. Bewusst KEINE
HTML-Datei in dieser Liste. */
+119 -19
View File
@@ -306,8 +306,41 @@
Deshalb ein weiter Radius mit vielen flachen Stufen: Der
Helligkeitsunterschied verteilt sich ueber die halbe Kachel und
liest sich als Schimmer statt als Kreis. */
/* Der Lichtkegel sitzt FEST in der Mitte des Elements und wird per
transform verschoben -- nicht ueber die Position des Verlaufs.
Das ist der Unterschied zwischen ruckelig und fluessig: Aendert
sich die Verlaufsposition, muss der Browser die Flaeche bei jeder
Zeigerbewegung neu zeichnen. Und weil darueber Glasflaechen mit
Rueckseiten-Unschaerfe liegen, muessen die gleich mit. Gemessen
waren es dadurch 17 Bilder pro Sekunde.
Eine Verschiebung per transform laeuft dagegen auf der
Grafikkarte: Der Verlauf wird einmal gezeichnet und danach nur
noch bewegt. */
width: 2200px;
height: 2200px;
inset: auto auto auto auto;
left: 50%;
top: 50%;
margin-left: -1100px;
margin-top: -1100px;
will-change: transform;
transform: translate3d(var(--vw-lx, 0px), var(--vw-ly, 0px), 0);
/* Die Bewegung wird vom Stilblatt geglaettet, nicht vom Skript.
Der Grund ist Rechenzeit: Jede Verschiebung der Lampe aendert den
Untergrund SAEMTLICHER Glaskacheln darueber -- und jede davon muss
daraufhin ihre Rueckseiten-Unschaerfe neu berechnen. Bei 60
Aktualisierungen je Sekunde faellt das 60-mal an.
Das Skript setzt die Position deshalb nur noch etwa 12-mal je
Sekunde. Die Uebergangszeit fuellt die Luecken dazwischen weich
auf -- fuer das Auge laeuft die Lampe unveraendert fluessig, der
Rechner macht ein Fuenftel der Arbeit. */
transition: transform .22s cubic-bezier(.25,.8,.35,1);
background: radial-gradient(
1100px circle at var(--vw-mx, 50%) var(--vw-my, 40%),
circle 1100px at 50% 50%,
rgb(var(--vw-farbe) / .34) 0%,
rgb(var(--vw-farbe) / .26) 22%,
rgb(var(--vw-farbe) / .16) 42%,
@@ -320,7 +353,20 @@
/* Erst zeigen, wenn der Zeiger wirklich da war -- sonst haengt beim
Laden ein Lichtfleck in der Bildmitte, den niemand dorthin gelegt
hat. */
.vw-lampe.vw-lampe-an { opacity: 1; }
/* Die Zeigerlampe ist ABGESCHALTET.
Sie war der zweite Teil der teuren Wechselwirkung: Ihre Bewegung
zwang jede Glasflaeche darueber zum Neurechnen. Zusammen mit dem
Glas kostete sie zwei Drittel der Bildrate.
Das Licht, das dem Zeiger folgt, gibt es weiterhin -- auf den
Kacheln selbst. Das ist der Effekt, der zaehlt, und er ist billig:
Er betrifft nur die Kachel unter dem Zeiger, nicht den ganzen
Bildschirm.
Die Regel bleibt bewusst stehen statt geloescht zu werden, damit
nachvollziehbar ist, dass es eine Entscheidung war. */
.vw-lampe { display: none; }
@media (prefers-reduced-motion: reduce) {
.vw-lampe { transition: none; }
@@ -355,6 +401,7 @@
background: rgb(var(--vw-farbe) / .75);
box-shadow: 0 0 10px 1px rgb(var(--vw-farbe) / .45);
opacity: 0;
will-change: transform, opacity;
animation: vw-schweben linear infinite;
}
@keyframes vw-schweben {
@@ -390,22 +437,49 @@
Wo der Browser backdrop-filter nicht kann, traegt die Farbe allein:
Deshalb ist der Hintergrund auch ohne Unschaerfe schon dicht genug.
Das ist kein Schoenheitsfehler, sondern der Rueckfall. */
#vw-bereich .wd-karte,
#vw-bereich .vw-karte,
#vw-bereich .vw-meld,
#vw-bereich .vw-detail-blatt,
#vw-bereich .wd-notiz,
#vw-bereich table {
-webkit-backdrop-filter: blur(22px) saturate(1.15);
backdrop-filter: blur(22px) saturate(1.15);
/* GLAS NUR AUF WENIGEN, GROSSEN FLAECHEN.
Eine Rueckseiten-Unschaerfe ist der teuerste Effekt auf dieser
Seite -- und ihre Kosten fallen pro Element an. Auf jeder Kachel
einer langen Liste bedeutet das: Bei jeder Zeigerbewegung rechnet
der Browser sie fuer jede Kachel einzeln neu.
Gemessen mit echter Mausbewegung, sechs Kacheln:
alles an 6,4 Bilder/s
ohne Glas 9,7
ohne Lampe 9,2
ohne Glas UND Lampe 16,4
Die beiden verstaerken sich gegenseitig: Die Lampe verschiebt den
Untergrund, woraufhin JEDE Glasflaeche darueber neu rechnen muss.
Die Kacheln bekommen deshalb einen dichteren Hintergrund statt
einer Unschaerfe. Der Unterschied ist gering -- die Buehne
schimmert weiterhin durch, nur ohne Weichzeichnung -- und ein
Arbeitswerkzeug, das bei jeder Mausbewegung ruckelt, ist kein
Gewinn an Schoenheit.
Das Detailblatt behaelt seine Unschaerfe: Davon gibt es immer nur
genau eines, und es liegt gross ueber der Seite. */
/* Ohne #vw-bereich davor: Das Detailblatt liegt NICHT im
Arbeitsbereich, sondern in einer eigenen Ueberlagerung
(#vw-detail). Mit dem Vorsatz griff die Regel nie -- der Test
meldete "keins", und die einzige Flaeche, die die Unschaerfe
wirklich verdient, hatte als einzige keine. */
.vw-detail-blatt {
-webkit-backdrop-filter: blur(14px) saturate(1.15);
backdrop-filter: blur(14px) saturate(1.15);
}
/* 14 statt 22 Punkte Unschaerfe. Optisch kaum zu unterscheiden --
die Struktur des Bildes ist schon bei 14 vollstaendig aufgeloest --
aber deutlich billiger: Die Rechenzeit einer solchen Unschaerfe
waechst mit dem Radius, und sie faellt bei JEDER Bewegung neu an. */
/* Die Reiter und die Bedienknoepfe schweben ebenfalls. */
#vw-bereich .vw-filter-knopf,
#vw-bereich .vw-wer {
-webkit-backdrop-filter: blur(16px) saturate(1.1);
backdrop-filter: blur(16px) saturate(1.1);
}
/* Reiter und Bedienknoepfe: dichte Flaeche statt Unschaerfe, aus
demselben Grund. Es sind sechs Reiter plus zwei Knoepfe -- also
acht Flaechen, die sonst bei jeder Bewegung mitrechnen. */
#vw-bereich .vw-filter-knopf { background: rgb(6 19 35 / .78); }
/* Meldungen wie "Wird geladen" oder "Nichts vorhanden" stehen frei
zwischen den Kacheln -- also direkt auf der Buehne. Sie bekommen
@@ -540,8 +614,21 @@
#vw-bereich .vw-meld {
background:
/* Die Flaeche: dicht genug, dass Text darauf ruhig steht. */
/* Deutlich dichter als zuvor (.52/.68).
Der Grund ist gemessen: Solange die Kachel durchscheinend ist,
muss der Browser das bildschirmfuellende Buehnenbild bei jeder
Neuzeichnung der Kachel mitrechnen. Ohne Buehnenbild stieg die
Bildrate von 24 auf 34,6 -- das Bild unter dem Glas war der
groesste verbliebene Posten.
Bei .92/.96 bleibt die Buehne rings um die Kacheln voll
sichtbar, durch die Kachel selbst schimmert sie nur noch als
Ahnung. Das kostet etwas Tiefe und bringt Fluessigkeit -- auf
einer Seite, auf der gearbeitet wird, ist das der richtige
Tausch. */
linear-gradient(180deg,
rgb(var(--wd-glas-rgb) / .52), rgb(6 19 35 / .68)) padding-box,
rgb(var(--wd-glas-rgb) / .92), rgb(6 19 35 / .96)) padding-box,
/* Die Kante: hier steckt der Zeiger drin. Der Verlauf sitzt an
der Mausposition, dadurch wandert der helle Punkt am Rand
entlang mit. */
@@ -4029,6 +4116,7 @@
if (!buehne) return;
var lampe = document.querySelector(".vw-lampe");
var lampeAn = false;
var lampeZuletzt = 0;
var offen = false, letzteX = 0, letzteY = 0;
document.addEventListener("pointermove", function (e) {
@@ -4038,6 +4126,7 @@
offen = true;
requestAnimationFrame(function () {
offen = false;
var zeit = Date.now();
/* -0,5 bis +0,5 -- der Nullpunkt liegt in der Bildschirmmitte. */
var px = (letzteX / window.innerWidth) - 0.5;
var py = (letzteY / window.innerHeight) - 0.5;
@@ -4048,9 +4137,20 @@
Anteilswert: Ein radialer Verlauf mit Prozentangaben
verschiebt sich mit der Elementgroesse und laege dann nicht
mehr unter dem Zeiger. */
if (lampe) {
lampe.style.setProperty("--vw-mx", letzteX + "px");
lampe.style.setProperty("--vw-my", letzteY + "px");
/* Die Lampe wird bewusst SELTENER nachgefuehrt als die Buehne.
Jede ihrer Bewegungen zwingt alle Glaskacheln darueber, ihre
Unschaerfe neu zu berechnen. Zwoelfmal je Sekunde reicht
voellig: Den Rest glaettet der Uebergang im Stilblatt, und
fuer das Auge bleibt die Bewegung fluessig. */
if (lampe && zeit - lampeZuletzt > 80) {
lampeZuletzt = zeit;
/* Versatz aus der Bildschirmmitte, nicht die Position selbst:
Die Lampe wird verschoben, nicht neu gezeichnet. */
lampe.style.setProperty("--vw-lx",
Math.round(letzteX - window.innerWidth / 2) + "px");
lampe.style.setProperty("--vw-ly",
Math.round(letzteY - window.innerHeight / 2) + "px");
if (!lampeAn) { lampe.classList.add("vw-lampe-an"); lampeAn = true; }
}
});