mirror of
http://172.20.10.11:3000/gitadmin/INSIGHT-MVP.git
synced 2026-06-24 23:56:40 +02:00
117 lines
3.4 KiB
TypeScript
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>
|
|
);
|
|
}
|