diff --git a/src/App.css b/src/App.css index ff0431c..f0d5e26 100644 --- a/src/App.css +++ b/src/App.css @@ -52,8 +52,28 @@ flex-direction: column; align-items: flex-end; justify-content: center; - gap: 4px; - margin-right: 4px; + margin-right: 8px; +} + +.digital-clock-wrapper { + position: relative; + display: flex; + flex-direction: column; + align-items: stretch; +} + +.digital-clock-header { + display: flex; + flex-direction: row; + align-items: baseline; + justify-content: space-between; + margin-bottom: 4px; +} + +.digital-clock-header-left { + display: flex; + flex-direction: row; + align-items: baseline; } .digital-clock-period { @@ -64,6 +84,15 @@ line-height: 1; } +.digital-clock-date { + font-family: Arial, sans-serif; + font-weight: 600; + color: #fff; + text-shadow: 0 0 10px rgba(255, 255, 255, 0.5); + line-height: 1; + white-space: nowrap; +} + .format-toggle-side { padding: 4px 8px; font-size: 12px; @@ -74,6 +103,7 @@ border-radius: 4px; cursor: pointer; transition: all 0.2s ease; + white-space: nowrap; } .format-toggle-side:hover { diff --git a/src/App.tsx b/src/App.tsx index a81899b..b861764 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,21 +5,32 @@ import './App.css' type ClockStyle = 'digital' | 'flip' | 'roll' -function DigitalClock({ time, size, period, is24Hour, onToggleFormat }: { time: string; size: number; period?: string; is24Hour: boolean; onToggleFormat: () => void }) { +interface DateInfo { + dateString: string + weekday: string +} + +function formatDate(date: Date): DateInfo { + const year = date.getFullYear() + const month = String(date.getMonth() + 1).padStart(2, '0') + const day = String(date.getDate()).padStart(2, '0') + const weekdays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'] + const weekday = weekdays[date.getDay()] + + return { + dateString: `${year}-${month}-${day}`, + weekday + } +} + +function DigitalClock({ time, size, period, is24Hour, onToggleFormat, dateInfo }: { time: string; size: number; period?: string; is24Hour: boolean; onToggleFormat: () => void; dateInfo: DateInfo }) { const fontSize = Math.floor(size * 0.95) - const periodSize = Math.floor(size * 0.25) + const periodSize = Math.floor(size * 0.20) + const dateSize = Math.floor(size * 0.20) return (
- {!is24Hour && period && ( - - {period} - - )}
-
- {time} +
+
+
+ {!is24Hour && period && ( + + {period} + + )} +
+ + {dateInfo.dateString} {dateInfo.weekday} + +
+
+ {time} +
) @@ -86,13 +117,14 @@ function App() { } const { timeString, period } = formatTime() + const dateInfo = formatDate(time) const renderClock = () => { switch (clockStyle) { case 'flip': - return + return case 'roll': - return + return case 'digital': default: return ( @@ -102,6 +134,7 @@ function App() { period={period} is24Hour={is24Hour} onToggleFormat={() => setIs24Hour(!is24Hour)} + dateInfo={dateInfo} /> ) } diff --git a/src/components/FlipClock.css b/src/components/FlipClock.css index ddc5271..d9d0123 100644 --- a/src/components/FlipClock.css +++ b/src/components/FlipClock.css @@ -10,8 +10,7 @@ flex-direction: column; align-items: flex-end; justify-content: center; - gap: 4px; - margin-right: 4px; + margin-right: 8px; } .flip-clock-period { @@ -32,6 +31,7 @@ border-radius: 4px; cursor: pointer; transition: all 0.2s ease; + white-space: nowrap; } .flip-format-toggle:hover { @@ -39,6 +39,34 @@ background-color: rgba(255, 255, 255, 0.15); } +.flip-clock-main { + display: flex; + flex-direction: column; + align-items: stretch; +} + +.flip-clock-header { + display: flex; + flex-direction: row; + align-items: baseline; + justify-content: space-between; + margin-bottom: 4px; +} + +.flip-clock-header-left { + display: flex; + flex-direction: row; + align-items: baseline; +} + +.flip-clock-period { + font-family: Arial, sans-serif; + font-weight: 600; + color: #fff; + text-shadow: 0 0 10px rgba(255, 255, 255, 0.5); + line-height: 1; +} + .flip-clock-container { display: flex; flex-direction: row; @@ -46,6 +74,15 @@ justify-content: center; } +.flip-clock-date { + font-family: Arial, sans-serif; + font-weight: 600; + color: #fff; + text-shadow: 0 0 10px rgba(255, 255, 255, 0.5); + line-height: 1; + white-space: nowrap; +} + .flip-clock-digit-group { display: flex; flex-direction: row; diff --git a/src/components/FlipClock.tsx b/src/components/FlipClock.tsx index b9a9ade..c1c1d9a 100644 --- a/src/components/FlipClock.tsx +++ b/src/components/FlipClock.tsx @@ -2,38 +2,31 @@ import { useState } from 'react'; import { FlipDigit } from './FlipDigit'; import './FlipClock.css'; +interface DateInfo { + dateString: string; + weekday: string; +} + interface FlipClockProps { time: string; // "HH:MM:SS" format size: number; + dateInfo: DateInfo; + period?: string; } -export function FlipClock({ time, size }: FlipClockProps) { +export function FlipClock({ time, size, dateInfo, period }: FlipClockProps) { const [is24Hour, setIs24Hour] = useState(true); - // Parse time and convert if needed + // Parse time - it's already in 12h format when period is provided const [hoursStr, minutes, seconds] = time.split(':'); - let hours = parseInt(hoursStr, 10); - let period: string | undefined; + const displayHours = hoursStr; - if (!is24Hour) { - period = hours >= 12 ? 'PM' : 'AM'; - hours = hours % 12 || 12; - } - - const displayHours = String(hours).padStart(2, '0'); - const periodSize = size * 0.25; + const periodSize = size * 0.20; + const dateSize = size * 0.20; return (
- {!is24Hour && period && ( - - {period} - - )}
-
- {/* Hours */} -
- - +
+
+
+ {period && ( + + {period} + + )} +
+ + {dateInfo.dateString} {dateInfo.weekday} +
+
+ {/* Hours */} +
+ + +
- - : - + + : + - {/* Minutes */} -
- - -
+ {/* Minutes */} +
+ + +
- - : - + + : + - {/* Seconds */} -
- - + {/* Seconds */} +
+ + +
diff --git a/src/components/FlipDigit.css b/src/components/FlipDigit.css index b5c850a..1f60955 100644 --- a/src/components/FlipDigit.css +++ b/src/components/FlipDigit.css @@ -3,6 +3,7 @@ border-radius: 4px; background-color: #1a1a1a; overflow: hidden; + perspective: 1000px; } .flip-digit-bg-top { diff --git a/src/components/FlipDigit.tsx b/src/components/FlipDigit.tsx index a540459..9f2ab23 100644 --- a/src/components/FlipDigit.tsx +++ b/src/components/FlipDigit.tsx @@ -33,11 +33,14 @@ export function FlipDigit({ value, size }: FlipDigitProps) { // Finish animation const finishAnimation = () => { - setTopRotation(0); - setBottomRotation(-90); - setFlipBottomValue(null); setIsAnimating(false); - animating.current = false; + + requestAnimationFrame(() => { + setTopRotation(0); + setBottomRotation(-90); + setFlipBottomValue(null); + animating.current = false; + }); }; // Start bottom animation after top completes @@ -64,7 +67,6 @@ export function FlipDigit({ value, size }: FlipDigitProps) { useEffect(() => { if (value !== bgTopValue && !animating.current) { animating.current = true; - setIsAnimating(true); // Save current value as flip top value setFlipTopValue(bgTopValue); @@ -73,13 +75,23 @@ export function FlipDigit({ value, size }: FlipDigitProps) { // Update bgTop immediately (revealed when top flips away) setBgTopValue(value); - // Reset rotation states + // First, ensure transition is disabled before resetting rotation + setIsAnimating(false); + + // Reset rotation states instantly (without transition) setTopRotation(0); setBottomRotation(-90); - // Start top animation after a brief delay to ensure state is set + // Wait for browser to apply the reset (without transition) requestAnimationFrame(() => { - setTopRotation(90); + // Now enable transition for the actual animation + setIsAnimating(true); + + // Wait one more frame to ensure transition is enabled + requestAnimationFrame(() => { + // Start top animation: flip from 0° to 90° + setTopRotation(90); + }); }); // After top animation completes, start bottom animation @@ -111,12 +123,12 @@ export function FlipDigit({ value, size }: FlipDigitProps) { const topFlipStyle = { height: `${halfHeight}px`, - transform: `perspective(1000px) rotateX(${topRotation}deg)`, + transform: `rotateX(${topRotation}deg)`, }; const bottomFlipStyle = { height: `${halfHeight}px`, - transform: `perspective(1000px) rotateX(${bottomRotation}deg)`, + transform: `rotateX(${bottomRotation}deg)`, }; const centerLineStyle = { diff --git a/src/components/RollClock.css b/src/components/RollClock.css index 31ba25b..7773d26 100644 --- a/src/components/RollClock.css +++ b/src/components/RollClock.css @@ -10,8 +10,7 @@ flex-direction: column; align-items: flex-end; justify-content: center; - gap: 4px; - margin-right: 4px; + margin-right: 8px; } .roll-clock-period { @@ -32,6 +31,7 @@ border-radius: 4px; cursor: pointer; transition: all 0.2s ease; + white-space: nowrap; } .roll-format-toggle:hover { @@ -45,6 +45,34 @@ align-items: center; } +.roll-clock-main { + display: flex; + flex-direction: column; + align-items: stretch; +} + +.roll-clock-header { + display: flex; + flex-direction: row; + align-items: baseline; + justify-content: space-between; + margin-bottom: 4px; +} + +.roll-clock-header-left { + display: flex; + flex-direction: row; + align-items: baseline; +} + +.roll-clock-period { + font-family: Arial, sans-serif; + font-weight: 600; + color: #fff; + text-shadow: 0 0 10px rgba(255, 255, 255, 0.5); + line-height: 1; +} + .roll-clock-container { display: flex; flex-direction: row; @@ -52,6 +80,15 @@ justify-content: center; } +.roll-clock-date { + font-family: Arial, sans-serif; + font-weight: 600; + color: #fff; + text-shadow: 0 0 10px rgba(255, 255, 255, 0.5); + line-height: 1; + white-space: nowrap; +} + .roll-clock-digit-group { display: flex; flex-direction: row; diff --git a/src/components/RollClock.tsx b/src/components/RollClock.tsx index 855a38f..e7a4005 100644 --- a/src/components/RollClock.tsx +++ b/src/components/RollClock.tsx @@ -2,29 +2,30 @@ import { useState } from 'react'; import { RollDigit } from './RollDigit'; import './RollClock.css'; +interface DateInfo { + dateString: string; + weekday: string; +} + interface RollClockProps { time: string; // "HH:MM:SS" format size: number; + dateInfo: DateInfo; + period?: string; } -export function RollClock({ time, size }: RollClockProps) { +export function RollClock({ time, size, dateInfo, period }: RollClockProps) { // Roll direction: 'down' = roll down, 'up' = roll up const [direction, setDirection] = useState<'down' | 'up'>('down'); const [is24Hour, setIs24Hour] = useState(true); - // Parse time and convert if needed + // Parse time - it's already in 12h format when period is provided const [hoursStr, minutes, seconds] = time.split(':'); - let hours = parseInt(hoursStr, 10); - let period: string | undefined; + const displayHours = hoursStr; - if (!is24Hour) { - period = hours >= 12 ? 'PM' : 'AM'; - hours = hours % 12 || 12; - } - - const displayHours = String(hours).padStart(2, '0'); const buttonSize = size * 0.25; - const periodSize = size * 0.25; + const periodSize = size * 0.20; + const dateSize = size * 0.20; // Toggle direction const toggleDirection = () => { @@ -34,14 +35,6 @@ export function RollClock({ time, size }: RollClockProps) { return (
- {!is24Hour && period && ( - - {period} - - )}
-
- {/* Hours */} -
- - +
+
+
+ {!is24Hour && period && ( + + {period} + + )} +
+ + {dateInfo.dateString} {dateInfo.weekday} +
+
+ {/* Hours */} +
+ + +
- - : - + + : + - {/* Minutes */} -
- - -
+ {/* Minutes */} +
+ + +
- - : - + + : + - {/* Seconds */} -
- - + {/* Seconds */} +
+ + +