|
@@ -0,0 +1,135 @@
|
|
|
+import './App.css';
|
|
|
+import { useEffect, useState } from 'react'
|
|
|
+
|
|
|
+const Timer = ({ timeInSeconds }) => {
|
|
|
+ const [time, setTime] = useState(timeInSeconds)
|
|
|
+ const [isPaused, setPaused] = useState(false)
|
|
|
+
|
|
|
+ const hours = Math.floor(time / (60 * 60))
|
|
|
+ const minutes = Math.floor((time % (60 * 60)) / 60)
|
|
|
+ const seconds = Math.ceil((time % (60 * 60)) % 60)
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ let interval = setInterval(() => {setTime(time => time - 1)}, 1000)
|
|
|
+ if (time === 0 || isPaused) {
|
|
|
+ clearInterval(interval)
|
|
|
+ }
|
|
|
+
|
|
|
+ return () => {clearInterval(interval)}
|
|
|
+ }, [time, isPaused])
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ setTime(timeInSeconds)
|
|
|
+ }, [timeInSeconds])
|
|
|
+
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <div>{hours}:{minutes}:{seconds}</div>
|
|
|
+ <button onClick={() => setPaused(!isPaused)}> астанавись/прадалжай </button>
|
|
|
+ </>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+const TimerControl = ({setTimeCB}) => {
|
|
|
+ const [hours, setHours] = useState('')
|
|
|
+ const [minutes, setMinutes] = useState('')
|
|
|
+ const [seconds, setSeconds] = useState('')
|
|
|
+
|
|
|
+ const timeToSeconds = (hours, minutes, seconds) => {
|
|
|
+ let time = (+hours * 60 * 60) + (+minutes * 60) + +seconds
|
|
|
+ return time
|
|
|
+ }
|
|
|
+
|
|
|
+ const valuesDisappear = () => {
|
|
|
+ setHours('')
|
|
|
+ setMinutes('')
|
|
|
+ setSeconds('')
|
|
|
+ }
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <input type="number" placeholder="hours" value={hours} onChange={(e) => setHours(e.target.value)}></input>
|
|
|
+ <input type="number" placeholder="minutes" value={minutes} onChange={(e) => setMinutes(e.target.value)}></input>
|
|
|
+ <input type="number" placeholder="seconds" value={seconds} onChange={(e) => setSeconds(e.target.value)}></input>
|
|
|
+ <button onClick={() => {valuesDisappear(); setTimeCB(timeToSeconds(hours, minutes, seconds))}}>Start</button>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+const TimerContainer = ({seconds, refresh, render: Render}) => {
|
|
|
+ const [time, setTime] = useState(seconds)
|
|
|
+ const [initialTime] = useState(performance.now())
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ let interval = setInterval(() => {
|
|
|
+ setTime(seconds - Math.floor((performance.now() - initialTime) / 1000))
|
|
|
+ }, refresh)
|
|
|
+
|
|
|
+ if (time <= 0) {
|
|
|
+ setTime(0)
|
|
|
+ clearInterval(interval)
|
|
|
+ }
|
|
|
+
|
|
|
+ return () => clearInterval(interval)
|
|
|
+ }, [time, initialTime, refresh, seconds])
|
|
|
+
|
|
|
+ useEffect(() => {
|
|
|
+ setTime(seconds)
|
|
|
+ }, [seconds])
|
|
|
+
|
|
|
+ return (
|
|
|
+ <Render seconds={time}/>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+const SecondsTimer = ({seconds}) => <h2>{seconds}</h2>
|
|
|
+
|
|
|
+const LCD = ({seconds: timeInSeconds}) => {
|
|
|
+ const hours = Math.floor(timeInSeconds / (60 * 60))
|
|
|
+ const minutes = Math.floor((timeInSeconds % (60 * 60)) / 60)
|
|
|
+ const seconds = Math.ceil((timeInSeconds % (60 * 60)) % 60)
|
|
|
+ return (
|
|
|
+ <div>{hours}:{minutes}:{seconds}</div>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+const Watch = ({seconds: timeInSeconds}) => {
|
|
|
+ const hours = Math.floor(timeInSeconds / (60 * 60))
|
|
|
+ const minutes = Math.floor((timeInSeconds % (60 * 60)) / 60)
|
|
|
+ const seconds = Math.ceil((timeInSeconds % (60 * 60)) % 60)
|
|
|
+
|
|
|
+ const ratioH = 360 / 12 / 60 / 60
|
|
|
+ const ratioM = 360 / 60 / 60
|
|
|
+ const ratioS = 360 / 60
|
|
|
+
|
|
|
+ const getAngle = (ratio, value) => (ratio * value)
|
|
|
+
|
|
|
+ return (
|
|
|
+ <>
|
|
|
+ <div>{hours}:{minutes}:{seconds}</div>
|
|
|
+ <div style={{position: 'relative'}}>
|
|
|
+ <img src='http://draw.asmer.fe.a-level.com.ua/ClockFace/ClockFace.png' style={{position: 'absolute'}} />
|
|
|
+ <img src='http://draw.asmer.fe.a-level.com.ua/ClockFace/ClockFace_H.png' style={{position: 'absolute', transform: `rotate(${getAngle(ratioH, timeInSeconds)}deg)`}}/>
|
|
|
+ <img src='http://draw.asmer.fe.a-level.com.ua/ClockFace/ClockFace_M.png' style={{position: 'absolute', transform: `rotate(${getAngle(ratioM, timeInSeconds)}deg)`}}/>
|
|
|
+ <img src='http://draw.asmer.fe.a-level.com.ua/ClockFace/ClockFace_S.png' style={{position: 'absolute', transform: `rotate(${getAngle(ratioS, timeInSeconds)}deg)`}} />
|
|
|
+ </div>
|
|
|
+ </>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function App() {
|
|
|
+ const [time, setTime] = useState(0)
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <Timer timeInSeconds={time} />
|
|
|
+ <TimerControl setTimeCB={seconds => setTime(seconds)}/>
|
|
|
+
|
|
|
+ <TimerContainer seconds={1800} refresh={100} render={SecondsTimer}/>
|
|
|
+ <TimerContainer seconds={5000} refresh={100} render={LCD}/>
|
|
|
+ <TimerContainer seconds={time} refresh={100} render={Watch}/>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
+export default App;
|