diff --git a/blickfang-ueber-handy.png b/blickfang-ueber-handy.png index c301e1b4..2d92ff88 100644 Binary files a/blickfang-ueber-handy.png and b/blickfang-ueber-handy.png differ diff --git a/blickfang-ueber-pc.png b/blickfang-ueber-pc.png index 8b3853c1..76de3a55 100644 Binary files a/blickfang-ueber-pc.png and b/blickfang-ueber-pc.png differ diff --git a/patch15.py b/patch15.py new file mode 100644 index 00000000..c3645c30 --- /dev/null +++ b/patch15.py @@ -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') diff --git a/pruef-aufgaben-computer.png b/pruef-aufgaben-computer.png index 96b96b93..f1e79778 100644 Binary files a/pruef-aufgaben-computer.png and b/pruef-aufgaben-computer.png differ diff --git a/pruef-aufgaben-handy.png b/pruef-aufgaben-handy.png index 8eed58e6..7b76f191 100644 Binary files a/pruef-aufgaben-handy.png and b/pruef-aufgaben-handy.png differ diff --git a/pruef-detail-computer.png b/pruef-detail-computer.png index 9d91ee29..5e519ab7 100644 Binary files a/pruef-detail-computer.png and b/pruef-detail-computer.png differ diff --git a/pruef-detail-handy.png b/pruef-detail-handy.png index 62c184c2..430ebc20 100644 Binary files a/pruef-detail-handy.png and b/pruef-detail-handy.png differ diff --git a/pruef-portal-aufgaben-computer.png b/pruef-portal-aufgaben-computer.png index 497925bc..6e54599d 100644 Binary files a/pruef-portal-aufgaben-computer.png and b/pruef-portal-aufgaben-computer.png differ diff --git a/pruef-portal-postfach-computer.png b/pruef-portal-postfach-computer.png index 48c4552c..1a88f9ad 100644 Binary files a/pruef-portal-postfach-computer.png and b/pruef-portal-postfach-computer.png differ diff --git a/pruef-portal-postfach-handy.png b/pruef-portal-postfach-handy.png index 4803c5cf..67193034 100644 Binary files a/pruef-portal-postfach-handy.png and b/pruef-portal-postfach-handy.png differ diff --git a/pruef-postfach-computer.png b/pruef-postfach-computer.png index 792c1d57..4d85a789 100644 Binary files a/pruef-postfach-computer.png and b/pruef-postfach-computer.png differ diff --git a/pruef-postfach-handy.png b/pruef-postfach-handy.png index f53972ca..a576e543 100644 Binary files a/pruef-postfach-handy.png and b/pruef-postfach-handy.png differ diff --git a/pruef-verwaltung-schmuck.mjs b/pruef-verwaltung-schmuck.mjs index d9eabcc5..c53d3011 100644 --- a/pruef-verwaltung-schmuck.mjs +++ b/pruef-verwaltung-schmuck.mjs @@ -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"); diff --git a/pruef-vw-schmuck-computer.png b/pruef-vw-schmuck-computer.png index 5e311125..d2623ef7 100644 Binary files a/pruef-vw-schmuck-computer.png and b/pruef-vw-schmuck-computer.png differ diff --git a/pruef-vw-schmuck-handy.png b/pruef-vw-schmuck-handy.png index d57584b9..2ce1eb20 100644 Binary files a/pruef-vw-schmuck-handy.png and b/pruef-vw-schmuck-handy.png differ diff --git a/pruef-widerruf-computer.png b/pruef-widerruf-computer.png index bc36015b..05604f3e 100644 Binary files a/pruef-widerruf-computer.png and b/pruef-widerruf-computer.png differ diff --git a/webdesign/sw.js b/webdesign/sw.js index 0e5060a4..e003a0ab 100644 --- a/webdesign/sw.js +++ b/webdesign/sw.js @@ -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. */ diff --git a/webdesign/verwaltung.html b/webdesign/verwaltung.html index a33e8faf..357c04cd 100644 --- a/webdesign/verwaltung.html +++ b/webdesign/verwaltung.html @@ -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; } } });