diff --git a/07-Stopwatch/le2yunji/Stopwatch.tsx b/07-Stopwatch/le2yunji/Stopwatch.tsx new file mode 100644 index 0000000..5b19dbc --- /dev/null +++ b/07-Stopwatch/le2yunji/Stopwatch.tsx @@ -0,0 +1,75 @@ +import { useEffect, useState } from "react"; +import formatTime from "./utils/formatTime"; +import "./style/stopwatch.css"; + +export default function Stopwatch() { + const [startTime, setStartTime] = useState(null); + const [isRunning, setIsRunning] = useState(false); + const [elapsedTime, setElapsedTime] = useState(0); + const [now, setNow] = useState(null); + + useEffect(() => { + if (!isRunning) return; + + let animationFrameId: number; + + const update = () => { + setNow(performance.now()); + animationFrameId = requestAnimationFrame(update); + }; + + animationFrameId = requestAnimationFrame(update); + + return () => cancelAnimationFrame(animationFrameId); + }, [isRunning]); + + const displayTime = + isRunning && startTime !== null && now !== null + ? elapsedTime + (now - startTime) + : elapsedTime; + + const handleStopwatch = () => { + if (!isRunning) { + const currentTime = performance.now(); + + setStartTime(currentTime); + setNow(currentTime); + setIsRunning(true); + return; + } + + if (startTime === null || now === null) return; + + const currentTime = performance.now(); + + setElapsedTime((prev) => prev + currentTime - startTime); + setStartTime(null); + setNow(null); + setIsRunning(false); + }; + + const handleReset = () => { + setElapsedTime(0); + setNow(null); + setStartTime(null); + setIsRunning(false); + }; + + return ( +
+
{formatTime(displayTime)}
+
+ + +
+
+ ); +} diff --git a/07-Stopwatch/le2yunji/style/stopwatch.css b/07-Stopwatch/le2yunji/style/stopwatch.css new file mode 100644 index 0000000..428ae05 --- /dev/null +++ b/07-Stopwatch/le2yunji/style/stopwatch.css @@ -0,0 +1,45 @@ +.stopwatch { + display: flex; + flex-direction: column; + align-items: center; + border-radius: 20px; + padding: 20px; + /* background-color: aqua; */ +} +.display { + font-variant-numeric: tabular-nums; + font-family: monospace; + font-size: 40px; + font-weight: 500; +} +button { + border: 0; + background: none; + padding: 0; + cursor: pointer; + font-size: 20px; +} +.button-set { + display: flex; + gap: 20px; + margin-top: 10px; +} +.button-stop { + border: 3px solid #ff303e; + padding: 10px; + border-radius: 10px; + background-color: #ff303e; + color: white; +} +.button-start { + border: 3px solid #2735ff; + padding: 10px; + border-radius: 10px; + background-color: #2735ff; + color: white; +} +.button-reset { + border: 1px solid; + padding: 5px; + border-radius: 10px; +} diff --git a/07-Stopwatch/le2yunji/utils/formatTime.ts b/07-Stopwatch/le2yunji/utils/formatTime.ts new file mode 100644 index 0000000..245b423 --- /dev/null +++ b/07-Stopwatch/le2yunji/utils/formatTime.ts @@ -0,0 +1,21 @@ +export default function formatTime(milliseconds: number) { + const hours = Math.floor(milliseconds / 1000 / 60 / 60); + const minutes = Math.floor((milliseconds / 1000 / 60) % 60); + const seconds = Math.floor((milliseconds / 1000) % 60); + const ms = Math.floor((milliseconds % 1000) / 10); + + const paddedMs = String(ms).padStart(2, "0"); + const paddedSeconds = String(seconds).padStart(2, "0"); + const paddedMinutes = String(minutes).padStart(2, "0"); + const paddedHours = String(hours).padStart(2, "0"); + + if (hours > 0) { + return `${paddedHours}h ${paddedMinutes}m ${paddedSeconds}s ${paddedMs}ms`; + } + + if (minutes > 0) { + return `${paddedMinutes}m ${paddedSeconds}s ${paddedMs}ms`; + } + + return `${paddedSeconds}s ${paddedMs}ms`; +}