Die Uhr bekommt Kometenschweife -- und die Koepfe gleiten, statt zu springen
Filipe: "nehm diese uhr bitte und passe sie so gut wie es geht an, auch die lichter von sekunden, minuten und stunden perfekt anpassen ... überrasch mich." ZWEI DINGE, DIE EINE UHR LEBENDIG MACHEN. 1. DER KOMETENSCHWEIF. 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. Gebaut als zweites, kuerzeres Segment ueber dem Bogen: die letzten rund 40 Grad, heller, mit runder Kappe. Ein Verlauf ENTLANG der Bahn geht in SVG nicht -- `linearGradient` folgt einer Geraden, keiner Kurve. Zwei Lagen sind der ehrliche Weg dorthin. Die Lage wird gerechnet, nicht geschaetzt: Bei Umfang U, Fortschritt a und Schweiflaenge s soll das Segment von (aU - s) bis aU liegen. Mit `dasharray: s, U-s` beginnt das sichtbare Stueck bei (U - offset), also ist offset = U*(1-a) + s. Die Laenge wird bei kurzen Boegen mitgekuerzt -- sonst haenge der Schweif am Anfang einer Minute am ENDE des Kreises, sichtbar als heller Strich bei zwoelf Uhr. 2. DIE KOEPFE GLEITEN. Bisher sprangen sie einmal je Sekunde. Jetzt uebernimmt der Browser die Bewegung dazwischen (Ueberblendung von 0,92 s) -- das Skript rechnet weiterhin nur EINMAL je Sekunde. Sechzig Bildberechnungen je Sekunde wuerden auf einer stundenlang offenen Seite den Rechner warm halten; diese Loesung kostet nichts. ZWEI FEHLER DABEI, BEIDE NUR IM BILD ZU SEHEN: Der erste Versuch rechnete die Winkel aus der vollen Unixzeit, damit sie immer weiterwachsen (sonst liefe die Ueberblendung einmal je Minute rueckwaerts durchs Zifferblatt). Ergebnis: `rotate(1.07333e+10deg)` -- Exponentialschreibweise, Nachkommastellen weg. Die Koepfe standen sichtbar neben ihren Boegen, der rote auf der anderen Seite der Uhr. Jetzt zaehlt eine eigene Uhr ab dem ersten Takt: klein genug zum Rechnen, wachsend genug fuer die Ueberblendung. Und ich hatte den Schweifen `rotate(-90deg)` gegeben, damit sie bei zwoelf beginnen -- das SVG ist aber bereits gedreht. Sie standen dadurch exakt eine Vierteldrehung daneben: oben links, waehrend die Boegen oben rechts endeten. NACHGEMESSEN, in Grad statt nach Augenmass: Kopf gegen Bogenende Abweichung 0,00° / 0,00° / 0,03° Schweifende gegen Bogen Abweichung 0,0° / 0,0° / 0,0° pruef-start-ansicht EXIT=0 (140), pruef-css-klassen EXIT=0 (28). Bei `prefers-reduced-motion` gleiten die Koepfe nicht -- sie springen dann wie vorher. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user