diff --git a/workspace/assets/css/heim.css b/workspace/assets/css/heim.css index 96c20fd5..5c888546 100644 --- a/workspace/assets/css/heim.css +++ b/workspace/assets/css/heim.css @@ -1659,3 +1659,77 @@ weniger Reiz. Die Bahnen bleiben in voller Farbe stehen. */ .uhr__sekunde, .uhr__minute, .uhr__stunde { filter: none; } } + +/* =================================================================== + DER KOMETENSCHWEIF (08.09.2026) + + Filipe: "auch die lichter von sekunden, minuten und stunden perfekt + anpassen, wie die läuft und aussieht soll wirklich mega perfekt sein + ... überrasch mich." + + WAS ER LEISTET: Ein Bogen in gleichmaessiger Farbe zeigt einen + STAND. Ein Bogen, der zur Spitze hin heller wird, zeigt eine + RICHTUNG -- man sieht ohne Nachdenken, wo "jetzt" ist und wohin es + laeuft. Bei drei Ringen uebereinander ist das der Unterschied + zwischen Ablesen und Erkennen. + + WARUM ZWEI LAGEN UND KEIN VERLAUF: Ein Farbverlauf ENTLANG einer + Kreisbahn geht in SVG nicht -- `linearGradient` folgt einer Geraden, + `radialGradient` einem Radius, keiner einer Bahn. Der ehrliche Weg + ist ein zweites, kuerzeres Segment ueber dem ersten. Es liegt vor + der Oberkante, damit die helle Kante auch auf dem Schweif sitzt. + + `stroke-linecap: round` nur hier, nicht am Hauptbogen: Die runde + Kappe markiert die Spitze. Am Hauptbogen wuerde sie auch den ANFANG + runden -- und der liegt bei zwoelf Uhr, wo eine runde Kappe wie ein + Fehler aussieht. + + AUGENSCHONEND: Der Schweif ist heller als der Bogen, aber nicht + greller -- er bleibt in derselben Farbfamilie und wird nicht + animiert. Was hier leuchtet, leuchtet ruhig. */ +/* KEINE EIGENE DREHUNG (Korrektur nach dem Bildschirmfoto). + + Ich hatte hier `transform: rotate(-90deg)` gesetzt, damit der + Schweif wie die Bahnen bei zwoelf Uhr beginnt. Das war doppelt: Das + ganze SVG `.uhr__ring` ist bereits um -90 Grad gedreht (siehe dort, + "Bei Null soll der Ring OBEN beginnen"). Die Schweife standen + dadurch exakt eine Vierteldrehung neben ihren Boegen -- im Bild oben + links, waehrend die Boegen oben rechts endeten. Die Rechnung war + richtig, die Drehung eine zu viel. */ +.uhr__spur-bahn { + fill: none; + stroke-linecap: round; + opacity: .95; +} +.uhr__spur-bahn[data-spur="sekunde"] { + stroke: #ffd2d6; stroke-width: 3; + filter: drop-shadow(0 0 3px rgba(255, 120, 134, .85)); +} +.uhr__spur-bahn[data-spur="minute"] { + stroke: #d8efff; stroke-width: 3.4; + filter: drop-shadow(0 0 3px rgba(130, 205, 255, .8)); +} +.uhr__spur-bahn[data-spur="stunde"] { + stroke: #ffe9c9; stroke-width: 4; + filter: drop-shadow(0 0 3px rgba(233, 190, 130, .75)); +} + +/* DIE KOEPFE GLEITEN, STATT ZU SPRINGEN. + + Die Anzeige selbst rechnet weiterhin nur EINMAL je Sekunde -- das + war eine bewusste Entscheidung (sechzig Bildberechnungen je Sekunde + halten auf einer stundenlang offenen Seite den Rechner warm). Die + Bewegung dazwischen uebernimmt der Browser: eine Ueberblendung von + knapp einer Sekunde, die genau bis zum naechsten Takt laeuft. + + Damit sieht die Perle aus wie ein gleitender Zeiger und kostet + trotzdem nichts. Voraussetzung ist, dass der Winkel WEITERZAEHLT + statt bei 360 Grad zurueckzuspringen -- sonst liefe die Ueberblendung + einmal je Minute rueckwaerts durch das ganze Zifferblatt. Genau das + macht start.js jetzt (fortlaufende Sekunden statt sk % 60). */ +.uhr__perle { + transition: transform .92s cubic-bezier(.33, 0, .2, 1); +} +@media (prefers-reduced-motion: reduce) { + .uhr__perle { transition: none; } +} diff --git a/workspace/assets/js/start.js b/workspace/assets/js/start.js index 5ae45711..c2b4e7da 100644 --- a/workspace/assets/js/start.js +++ b/workspace/assets/js/start.js @@ -379,11 +379,42 @@ for (const welcher of ['sekunde', 'minute', 'stunde']) { lagen[welcher] = [...document.querySelectorAll(`[data-ring="${welcher}"]`)]; } + /* DER SCHWEIF (08.09.2026): die letzten Grad vor der Spitze, heller. + + Gerechnet, nicht geschaetzt. Ein Kreis mit Umfang U, Fortschritt + a (0..1) und Schweiflaenge s soll ein Segment von (a*U - s) bis + a*U zeigen. Mit `dasharray: s, U-s` beginnt das sichtbare Stueck + bei (U - offset), also gilt + + U - offset = a*U - s -> offset = U*(1-a) + s + + Die Laenge wird bei kurzen Boegen mitgekuerzt: Am Anfang einer + Minute ist der Bogen selbst noch kuerzer als der Schweif, und ein + Schweif ohne Bogen darunter haenge sonst am Ende des Kreises -- + sichtbar als heller Strich bei zwoelf Uhr, wo nichts sein darf. */ + const SCHWEIF = { sekunde: 30, minute: 26, stunde: 22 }; // Bogenlaenge + const spuren = {}; + for (const welcher of ['sekunde', 'minute', 'stunde']) { + spuren[welcher] = document.querySelector(`[data-spur="${welcher}"]`); + } + const setze = (welcher, anteil) => { - const wert = String(UMFANG[welcher] * (1 - anteil)); + const U = UMFANG[welcher]; + const wert = String(U * (1 - anteil)); for (const el of lagen[welcher]) el.style.strokeDashoffset = wert; + + const spur = spuren[welcher]; + if (!spur) return; + const s = Math.min(SCHWEIF[welcher], U * anteil); + if (s <= 0.5) { spur.style.opacity = '0'; return; } /* nichts zu zeigen */ + spur.style.opacity = ''; + spur.style.strokeDasharray = `${s} ${U - s}`; + spur.style.strokeDashoffset = String(U * (1 - anteil) + s); }; + /* Der Nullpunkt der eigenen Uhr -- siehe unten bei den Koepfen. */ + let nullpunkt = null, startWinkel = 0, minStart = 0, stdStart = 0, sekStart = 0; + const takt = () => { const j = new Date(); const std = j.getHours(), min = j.getMinutes(), sk = j.getSeconds(); @@ -409,16 +440,58 @@ sechzig. Auf einer Seite, die stundenlang offen steht, ist das der Unterschied zwischen einem warmen und einem kalten Rechner. */ + /* DIE KOEPFE ZAEHLEN WEITER, STATT ZURUECKZUSPRINGEN. + + Sie gleiten jetzt (Ueberblendung in heim.css) statt zu + springen. Damit das funktioniert, darf der Winkel bei 59 -> 0 + Sekunden NICHT von 354 auf 0 fallen -- die Ueberblendung liefe + sonst einmal je Minute rueckwaerts durch das ganze + Zifferblatt. Gerechnet wird deshalb mit der fortlaufenden Zeit: + Der Winkel waechst immer weiter, und 366 Grad sieht genauso aus + wie 6, nur dass der Weg dorthin vorwaerts fuehrt. + + Die Anzeige selbst rechnet weiterhin nur einmal je Sekunde -- + die Bewegung dazwischen macht der Browser, nicht das Skript. */ + /* SEIT DEM ERSTEN TAKT, NICHT SEIT 1970. + + Der erste Versuch rechnete mit der vollen Unixzeit. Ergebnis im + Bildschirmfoto: `rotate(1.07333e+10deg)` -- eine Zahl in + Exponentialschreibweise, bei der die Nachkommastellen laengst + weg sind. Die Perlen standen sichtbar NEBEN dem Ende ihrer + Boegen, die rote sogar auf der anderen Seite des Zifferblatts. + Gerechnet war es richtig, darstellbar nicht. + + Jetzt zaehlt eine eigene Uhr ab dem ersten Takt: Startwinkel ist + der echte Stand beim Laden, danach kommen 6 Grad je Sekunde + dazu. Die Zahl bleibt klein (ein Tag sind 518 400 Grad), waechst + aber weiter -- und nur das braucht die Ueberblendung, damit sie + nie rueckwaerts laeuft. */ + if (nullpunkt === null) { + nullpunkt = Math.floor(j.getTime() / 1000); + startWinkel = sk * 6; + minStart = min; stdStart = std; sekStart = sk; + } + const seit = Math.floor(j.getTime() / 1000) - nullpunkt; + const winkelSek = startWinkel + seit * 6; const perle = $('uhr-perle'); - if (perle) perle.style.transform = `rotate(${sk * 6}deg)`; + if (perle) perle.style.transform = `rotate(${winkelSek.toFixed(2)}deg)`; /* Die beiden anderen Koepfe laufen mit IHREN Ringen -- die Minute nimmt die Sekunden anteilig mit, die Stunde die Minuten. Sonst staende der Kopf neben dem Ende seines Bogens, und genau das sieht man sofort. */ + /* Minute und Stunde ebenso: Startstand plus das, was seither + vergangen ist. Sie tragen die kleineren Einheiten anteilig mit, + damit ihr Kopf nie neben dem Ende ihres Bogens steht. */ const perleMin = $('uhr-perle-minute'); - if (perleMin) perleMin.style.transform = `rotate(${(min + sk / 60) * 6}deg)`; + if (perleMin) { + const start = (minStart + sekStart / 60) * 6; + perleMin.style.transform = `rotate(${(start + seit * 0.1).toFixed(3)}deg)`; + } const perleStd = $('uhr-perle-stunde'); - if (perleStd) perleStd.style.transform = `rotate(${((std % 12) + min / 60) * 30}deg)`; + if (perleStd) { + const start = ((stdStart % 12) + minStart / 60) * 30; + perleStd.style.transform = `rotate(${(start + seit / 120).toFixed(4)}deg)`; + } setze('minute', (min + sk / 60) / 60); setze('stunde', ((std % 12) + min / 60) / 12); diff --git a/workspace/start.html b/workspace/start.html index 426ea780..1149014f 100644 --- a/workspace/start.html +++ b/workspace/start.html @@ -286,6 +286,34 @@ + + + + + + +