Skip to content

Commit 079e02f

Browse files
authored
Merge pull request #1 from Ltuan126/dev
ok
2 parents 09de2d6 + 7e9972f commit 079e02f

6 files changed

Lines changed: 172 additions & 36 deletions

File tree

src/App.jsx

Lines changed: 7 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,12 @@
1-
import { useState } from 'react'
2-
import reactLogo from './assets/react.svg'
3-
import viteLogo from '/vite.svg'
4-
import './App.css'
1+
import Stopwatch from './Stopwatch';
52

63
function App() {
7-
const [count, setCount] = useState(0)
8-
94
return (
10-
<>
11-
<div>
12-
<a href="https://vite.dev" target="_blank">
13-
<img src={viteLogo} className="logo" alt="Vite logo" />
14-
</a>
15-
<a href="https://react.dev" target="_blank">
16-
<img src={reactLogo} className="logo react" alt="React logo" />
17-
</a>
18-
</div>
19-
<h1>Vite + React</h1>
20-
<div className="card">
21-
<button onClick={() => setCount((count) => count + 1)}>
22-
count is {count}
23-
</button>
24-
<p>
25-
Edit <code>src/App.jsx</code> and save to test HMR
26-
</p>
27-
</div>
28-
<p className="read-the-docs">
29-
Click on the Vite and React logos to learn more
30-
</p>
31-
</>
32-
)
5+
<div className="container">
6+
<h1 className="heading">STOP WATCH</h1>
7+
<Stopwatch />
8+
</div>
9+
);
3310
}
3411

35-
export default App
12+
export default App;

src/Stopwatch.jsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React, { useState, useRef, useEffect } from 'react';
2+
3+
export default function Stopwatch() {
4+
const [running, setRunning] = useState(false);
5+
const [elapsedTime, setElapsedTime] = useState(0);
6+
const timerRef = useRef(null);
7+
const startTimeRef = useRef(null);
8+
9+
useEffect(() => {
10+
if (running) {
11+
startTimeRef.current = Date.now() - elapsedTime;
12+
timerRef.current = setInterval(() => {
13+
setElapsedTime(Date.now() - startTimeRef.current);
14+
}, 10);
15+
} else {
16+
clearInterval(timerRef.current);
17+
}
18+
return () => clearInterval(timerRef.current);
19+
}, [running]);
20+
21+
const formatTime = (ms) => {
22+
const totalSeconds = Math.floor(ms / 1000);
23+
const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0');
24+
const seconds = String(totalSeconds % 60).padStart(2, '0');
25+
const milliseconds = String(ms % 1000).padStart(3, '0').slice(0, 2);
26+
return `${minutes}:${seconds}:${milliseconds}`;
27+
};
28+
29+
const reset = () => {
30+
setRunning(false);
31+
setElapsedTime(0);
32+
};
33+
34+
return (
35+
<>
36+
<div id="timer" className={running ? 'running' : ''}>
37+
{formatTime(elapsedTime)}
38+
</div>
39+
<div className="btn">
40+
<button onClick={() => setRunning(true)}>Start</button>
41+
<button onClick={() => setRunning(false)}>Stop</button>
42+
<button onClick={reset}>Reset</button>
43+
</div>
44+
</>
45+
);
46+
}

src/main.jsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { StrictMode } from 'react'
2-
import { createRoot } from 'react-dom/client'
3-
import './index.css'
4-
import App from './App.jsx'
1+
import { StrictMode } from 'react';
2+
import { createRoot } from 'react-dom/client';
3+
import './style.css';
4+
import App from './App.jsx';
55

66
createRoot(document.getElementById('root')).render(
77
<StrictMode>
88
<App />
9-
</StrictMode>,
10-
)
9+
</StrictMode>
10+
);

src/react.svg

Lines changed: 1 addition & 0 deletions
Loading

src/style.css

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
body {
2+
margin: 0;
3+
background-color: #06080f;
4+
font-family: Arial, sans-serif;
5+
}
6+
7+
.container {
8+
display: flex;
9+
flex-direction: column;
10+
align-items: center;
11+
justify-content: center;
12+
height: 100vh;
13+
}
14+
15+
.heading {
16+
font-size: 120px;
17+
font-weight: bold;
18+
color: #6be6cb;
19+
margin-top: 0;
20+
margin-bottom: 20px;
21+
text-align: center;
22+
}
23+
24+
#timer {
25+
position: relative;
26+
font-size: 120px;
27+
font-weight: bold;
28+
margin-bottom: 20px;
29+
color: #c8cbd0;
30+
overflow: hidden;
31+
border: 4px solid transparent;
32+
border-radius: 12px;
33+
padding: 20px 40px;
34+
background-color: #10141c;
35+
box-shadow: 0 0 15px rgba(107, 230, 203, 0.5);
36+
}
37+
38+
#timer::before {
39+
content: "";
40+
position: absolute;
41+
top: 0;
42+
left: -75%;
43+
width: 50%;
44+
height: 100%;
45+
background: linear-gradient(
46+
120deg,
47+
rgba(255, 255, 255, 0) 0%,
48+
rgba(255, 255, 255, 0.4) 50%,
49+
rgba(255, 255, 255, 0) 100%
50+
);
51+
transform: skewX(-20deg);
52+
pointer-events: none;
53+
opacity: 0;
54+
}
55+
56+
#timer.running::before {
57+
animation: shine 2s infinite;
58+
opacity: 1;
59+
}
60+
61+
#timer.running {
62+
border-image: linear-gradient(135deg, #00ffff, #ff00ff, #00ff00, #ffff00);
63+
border-image-slice: 1;
64+
animation: borderFlow 5s linear infinite;
65+
}
66+
67+
@keyframes shine {
68+
0% {
69+
left: -75%;
70+
}
71+
100% {
72+
left: 125%;
73+
}
74+
}
75+
76+
@keyframes borderFlow {
77+
0% {
78+
border-image-source: linear-gradient(
79+
135deg,
80+
#00ffff,
81+
#ff00ff,
82+
#00ff00,
83+
#ffff00
84+
);
85+
}
86+
100% {
87+
border-image-source: linear-gradient(
88+
495deg,
89+
#00ffff,
90+
#ff00ff,
91+
#00ff00,
92+
#ffff00
93+
);
94+
}
95+
}
96+
97+
.btn button {
98+
font-size: 24px;
99+
padding: 15px 30px;
100+
margin: 10px;
101+
border: none;
102+
border-radius: 8px;
103+
background-color: #509eb4;
104+
color: white;
105+
cursor: pointer;
106+
transition: background-color 0.3s;
107+
}
108+
109+
.btn button:hover {
110+
background-color: #47bddd;
111+
}

timer-clock_react-discuss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Subproject commit d1414f97ffc3211cef31e367e2bb061595536406

0 commit comments

Comments
 (0)