INSIGHT-MVP/packages/frontend/src/components/AnalogClock.tsx

117 lines
3.4 KiB
TypeScript

import { useEffect, useState } from 'react';
import styles from './AnalogClock.module.css';
// ── SVG-Konstanten ────────────────────────────────────────────────────────────
const CX = 50;
const CY = 50;
const R = 43;
/** Berechnet die Spitzenkoordinaten eines Uhrzeigers. */
function handTip(
cx: number,
cy: number,
deg: number,
length: number,
): { x: number; y: number } {
// 0° = 12 Uhr, im Uhrzeigersinn
const rad = ((deg - 90) * Math.PI) / 180;
return {
x: cx + length * Math.cos(rad),
y: cy + length * Math.sin(rad),
};
}
// ── Komponente ────────────────────────────────────────────────────────────────
export function AnalogClock() {
const [now, setNow] = useState(() => new Date());
useEffect(() => {
const id = setInterval(() => setNow(new Date()), 1000);
return () => clearInterval(id);
}, []);
const h = now.getHours() % 12;
const m = now.getMinutes();
const s = now.getSeconds();
const hourDeg = h * 30 + m * 0.5; // 360° / 12h
const minuteDeg = m * 6 + s * 0.1; // 360° / 60min
const secondDeg = s * 6; // 360° / 60s
const hTip = handTip(CX, CY, hourDeg, 26);
const mTip = handTip(CX, CY, minuteDeg, 34);
const sTip = handTip(CX, CY, secondDeg, 37);
const sBase = handTip(CX, CY, secondDeg + 180, 10); // Gegengewicht
const timeStr = now.toLocaleTimeString('de-DE', {
hour: '2-digit',
minute: '2-digit',
});
const dateStr = now.toLocaleDateString('de-DE', {
weekday: 'long',
day: '2-digit',
month: 'long',
year: 'numeric',
});
return (
<div className={styles.root}>
<svg
viewBox="0 0 100 100"
className={styles.svg}
aria-label={`Uhrzeit: ${timeStr}`}
role="img"
>
{/* Ziffernblatt */}
<circle cx={CX} cy={CY} r={R} className={styles.face} />
<circle cx={CX} cy={CY} r={R} fill="none" className={styles.border} />
{/* Stundenmarkierungen */}
{Array.from({ length: 12 }, (_, i) => {
const angle = (i * 30 - 90) * (Math.PI / 180);
const big = i % 3 === 0;
const outer = R - 1.5;
const inner = outer - (big ? 6 : 3.5);
return (
<line
key={i}
x1={CX + outer * Math.cos(angle)}
y1={CY + outer * Math.sin(angle)}
x2={CX + inner * Math.cos(angle)}
y2={CY + inner * Math.sin(angle)}
className={big ? styles.tickMajor : styles.tickMinor}
/>
);
})}
{/* Stundenzeiger */}
<line
x1={CX} y1={CY}
x2={hTip.x} y2={hTip.y}
className={styles.hourHand}
/>
{/* Minutenzeiger */}
<line
x1={CX} y1={CY}
x2={mTip.x} y2={mTip.y}
className={styles.minuteHand}
/>
{/* Sekundenzeiger mit Gegengewicht */}
<line
x1={sBase.x} y1={sBase.y}
x2={sTip.x} y2={sTip.y}
className={styles.secondHand}
/>
{/* Mittelpunkt */}
<circle cx={CX} cy={CY} r={2.5} className={styles.centerDot} />
</svg>
<div className={styles.dateText}>{dateStr}</div>
</div>
);
}