"use client"; interface AnalogClockProps { time: Date; theme: "light" | "dark" | "custom"; timezone?: string; customColor?: string; // 사용자 지정 색상 } /** * 아날로그 시계 컴포넌트 * - SVG 기반 아날로그 시계 * - 시침, 분침, 초침 애니메이션 * - 테마별 색상 지원 * - 타임존 표시 */ export function AnalogClock({ time, theme, timezone, customColor }: AnalogClockProps) { const hours = time.getHours() % 12; const minutes = time.getMinutes(); const seconds = time.getSeconds(); // 각도 계산 (12시 방향을 0도로, 시계방향으로 회전) const secondAngle = seconds * 6 - 90; // 6도씩 회전 (360/60) const minuteAngle = minutes * 6 + seconds * 0.1 - 90; // 6도씩 + 초당 0.1도 const hourAngle = hours * 30 + minutes * 0.5 - 90; // 30도씩 + 분당 0.5도 // 테마별 색상 const colors = getThemeColors(theme, customColor); // 타임존 라벨 const timezoneLabel = timezone ? getTimezoneLabel(timezone) : ""; return (
{/* 시계판 배경 */} {/* 눈금 표시 */} {[...Array(60)].map((_, i) => { const angle = (i * 6 - 90) * (Math.PI / 180); const isHour = i % 5 === 0; const startRadius = isHour ? 85 : 90; const endRadius = 95; return ( ); })} {/* 숫자 표시 (12시, 3시, 6시, 9시) */} {[12, 3, 6, 9].map((num, idx) => { const angle = (idx * 90 - 90) * (Math.PI / 180); const radius = 70; const x = 100 + radius * Math.cos(angle); const y = 100 + radius * Math.sin(angle); return ( {num} ); })} {/* 시침 (짧고 굵음) */} {/* 분침 (중간 길이) */} {/* 초침 (가늘고 긴) */} {/* 중심점 */} {/* 타임존 표시 */} {timezoneLabel && (
{timezoneLabel}
)}
); } /** * 타임존 라벨 반환 */ function getTimezoneLabel(timezone: string): string { const timezoneLabels: Record = { "Asia/Seoul": "서울 (KST)", "Asia/Tokyo": "도쿄 (JST)", "Asia/Shanghai": "베이징 (CST)", "America/New_York": "뉴욕 (EST)", "America/Los_Angeles": "LA (PST)", "Europe/London": "런던 (GMT)", "Europe/Paris": "파리 (CET)", "Australia/Sydney": "시드니 (AEDT)", }; return timezoneLabels[timezone] || timezone.split("/")[1]; } /** * 테마별 색상 반환 */ function getThemeColors(theme: string, customColor?: string) { if (theme === "custom" && customColor) { // 사용자 지정 색상 사용 (약간 밝게/어둡게 조정) const lighterColor = adjustColor(customColor, 40); const darkerColor = adjustColor(customColor, -40); return { background: lighterColor, border: customColor, tick: customColor, number: darkerColor, hourHand: darkerColor, minuteHand: customColor, secondHand: "#ef4444", center: darkerColor, }; } const themes = { light: { background: "#ffffff", border: "#d1d5db", tick: "#9ca3af", number: "#374151", hourHand: "#1f2937", minuteHand: "#4b5563", secondHand: "#ef4444", center: "#1f2937", }, dark: { background: "#1f2937", border: "#4b5563", tick: "#6b7280", number: "#f9fafb", hourHand: "#f9fafb", minuteHand: "#d1d5db", secondHand: "#ef4444", center: "#f9fafb", }, custom: { background: "#e0e7ff", border: "#6366f1", tick: "#818cf8", number: "#4338ca", hourHand: "#4338ca", minuteHand: "#6366f1", secondHand: "#ef4444", center: "#4338ca", }, }; return themes[theme as keyof typeof themes] || themes.light; } /** * 색상 밝기 조정 */ function adjustColor(color: string, amount: number): string { const clamp = (num: number) => Math.min(255, Math.max(0, num)); const hex = color.replace("#", ""); const r = parseInt(hex.substring(0, 2), 16); const g = parseInt(hex.substring(2, 4), 16); const b = parseInt(hex.substring(4, 6), 16); const newR = clamp(r + amount); const newG = clamp(g + amount); const newB = clamp(b + amount); return `#${newR.toString(16).padStart(2, "0")}${newG.toString(16).padStart(2, "0")}${newB.toString(16).padStart(2, "0")}`; }