|
@@ -201,7 +201,7 @@ class PasswordConfirm extends React.Component {
|
|
|
Напишите компонент, в который передается через props количество секунд, а компонент при этом реализует обратный отсчет раз в секунду уменьшая количество секунд на 1. Останавливается на 0. Добавьте в компонент кнопку паузы.
|
|
|
Компонент должен отображать часы, минуты и секунды.*/
|
|
|
|
|
|
-function Timer({pauseState = true, sec}){
|
|
|
+function Timer({pauseState = false, sec}){
|
|
|
const [pause, setPause] = useState(pauseState)
|
|
|
const [time, setTime] = useState(sec)
|
|
|
|
|
@@ -228,7 +228,7 @@ function Timer({pauseState = true, sec}){
|
|
|
/*TimerControl
|
|
|
Напишите компонент, с тремя полями ввода (часы, минуты и секунды) и кнопкой Start, по которой будет стартовать компонент Timer*/
|
|
|
|
|
|
-function Timer2({sec=0}){
|
|
|
+function Timer2({sec}){
|
|
|
const [pause, setPause] = useState(false)
|
|
|
const [time, setTime] = useState(sec)
|
|
|
console.log('Timer2' , time)
|
|
@@ -238,10 +238,10 @@ function Timer2({sec=0}){
|
|
|
const timeoutRef = useRef()
|
|
|
|
|
|
useEffect(() => {
|
|
|
- if (time > 0 && pause === false) {
|
|
|
+ if (time.value > 0 && pause === false) {
|
|
|
timeoutRef.current = setTimeout(() => {
|
|
|
- console.log(time)
|
|
|
- setTime(time - 1000)
|
|
|
+ console.log(time.value)
|
|
|
+ setTime({value: time.value - 1000})
|
|
|
}, 1000)
|
|
|
console.log('timeout', timeoutRef.current)
|
|
|
}
|
|
@@ -249,29 +249,29 @@ function Timer2({sec=0}){
|
|
|
|
|
|
let pauseButtonText = pause ? "продолжить отсчёт" : "пауза"
|
|
|
|
|
|
-
|
|
|
+ let hours = Math.floor(time.value / (60 * 60 * 1000))
|
|
|
+ let minutes = Math.floor((time.value - (hours*60*60*1000))/(60*1000))
|
|
|
+ let seconds = Math.floor((time.value - (hours*60*60*1000) - (minutes*60*1000))/1000)
|
|
|
return (
|
|
|
<div>
|
|
|
- <div>{time/1000}</div>
|
|
|
+ <span>{hours}:</span><span>{minutes}:</span><span>{seconds}</span>
|
|
|
<button onClick={()=>{console.log('pause', timeoutRef.current); clearTimeout(timeoutRef.current); setPause(!pause)}}>{pauseButtonText}</button>
|
|
|
</div>
|
|
|
)
|
|
|
}
|
|
|
|
|
|
function TimerControl({f}) {
|
|
|
- const [hours, setHours] = useState()
|
|
|
- const [minutes, setMinutes] = useState()
|
|
|
- const [seconds, setSeconds] = useState()
|
|
|
+ const [hours, setHours] = useState(0)
|
|
|
+ const [minutes, setMinutes] = useState(0)
|
|
|
+ const [seconds, setSeconds] = useState(0)
|
|
|
|
|
|
|
|
|
- console.log(seconds)
|
|
|
- const setTimer = () => {
|
|
|
|
|
|
- console.log(seconds)
|
|
|
- let value = seconds * 1000
|
|
|
+ const setTimer = () => {
|
|
|
+ let value = seconds * 1000 + minutes * 60 * 1000 + hours * 60 * 60 * 1000
|
|
|
console.log(value)
|
|
|
console.log(f)
|
|
|
- f(value)
|
|
|
+ f({value})
|
|
|
}
|
|
|
|
|
|
|
|
@@ -290,9 +290,104 @@ function TimerControl({f}) {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+/*TimerContainer
|
|
|
+const SecondsTimer = ({seconds}) => <h2>{seconds}</h2>
|
|
|
+SecondsTimer в данном случае играет роль presentation компонента, который сам ничего не умеет делать, а просто является шаблоном для отображения своих props в удобном для пользователя виде.
|
|
|
+Реализуйте контейнерный компонент, который будет обеспечивать состояние и логику для любого таймера:
|
|
|
+<TimerContainer seconds={1800} refresh={100} render={SecondsTimer}/>
|
|
|
+TimerContainer должен:
|
|
|
+воспринимать три пропса:
|
|
|
+seconds - секунды для обратного отсчета
|
|
|
+refresh - периодичность обновления таймера в миллисекундах
|
|
|
+render - компонент для отрисовки, которому передается текущее время
|
|
|
+Время вычисляется не по количеству обновлений, а по разности между стартовым и текущим моментом. Иначе таймер будет очень неточен
|
|
|
+так как JSX понимает переменные с маленькой буквы не как компоненты-функции, а как тэги HTML, переприсвойте props render в переменную с большой буквы и используйте её в JSX, как имя компонента, передавая пропс seconds.
|
|
|
+Так как при любом обновлении состояния функция-компонент, как и любая другая функция, запускается целиком используйте setInterval в useEffect*/
|
|
|
+
|
|
|
+const SecondsTimer = ({seconds}) => <h2>{seconds}</h2>
|
|
|
+
|
|
|
+function TimerContainer({seconds, refresh, render}) {
|
|
|
+ const [timeStart, setTimeStart] = useState((new Date()).getTime())
|
|
|
+ const [timeNow, setTimeNow] = useState((new Date()).getTime())
|
|
|
+ const [timerIsActiv, settimerActiv] = useState(true)
|
|
|
+
|
|
|
+
|
|
|
+ let time = (seconds - (timeNow - timeStart))/1000
|
|
|
+ console.log('time' , time)
|
|
|
+ if (time<0)time=0
|
|
|
+
|
|
|
+ useEffect(
|
|
|
+ ()=>{
|
|
|
+ if (time <= 0){
|
|
|
+ console.log("time < 0", timeNow)
|
|
|
+ settimerActiv(false)
|
|
|
+ setTimeNow(seconds + timeStart)
|
|
|
+ }
|
|
|
+ }, [timeNow])
|
|
|
+
|
|
|
+
|
|
|
+ useEffect(
|
|
|
+ ()=>{
|
|
|
+ if(timerIsActiv) {
|
|
|
+ const intervalId = setInterval(()=>{
|
|
|
+ console.log(timeNow)
|
|
|
+ setTimeNow((new Date()).getTime())
|
|
|
+ }
|
|
|
+ , refresh)
|
|
|
+ return ()=>clearInterval(intervalId)
|
|
|
+ }
|
|
|
+ }, [timerIsActiv])
|
|
|
+
|
|
|
+ const Render = render
|
|
|
+ return (
|
|
|
+ <Render seconds={time} timerIsActiv={timerIsActiv} settimerActiv={settimerActiv}/>
|
|
|
+)}
|
|
|
+
|
|
|
+/*LCD
|
|
|
+Сделайте из компонента Timer presentation компонент без state, прикрутите его к TimerContainer*/
|
|
|
+
|
|
|
+function TimerPresentation({seconds, timerIsActiv = false, settimerActiv}){
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div>
|
|
|
+ <div>{seconds}</div>
|
|
|
+ <button onClick={()=>{settimerActiv(!timerIsActiv)}}>пауза</button>
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+/*Watch
|
|
|
+Реализуйте часы со стрелками в качестве presentation компонента:
|
|
|
+квадратный блок-контейнер
|
|
|
+стрелки и, возможно, цифры позиционируются с помощью transform: rotate(УГОЛdeg)
|
|
|
+В верстке используйте position absolute для накладывания блоков стрелок и цифр друг на друга (это даст общий центр вращения)
|
|
|
+для корректного центра вращения блок со стрелкой или цифрой должен быть шириной с родительский квадратный блок
|
|
|
+есть еще всякий css (text-orientation) для вращения цифр внутри повернутого блока*/
|
|
|
+
|
|
|
+function Watch({seconds}) {
|
|
|
+ let hours = seconds / 3600
|
|
|
+ console.log('hours' , hours)
|
|
|
+ let hDeg = hours * 30
|
|
|
+ console.log('hDeg' , hDeg)
|
|
|
+ let minutes = (seconds % 3600) / 60
|
|
|
+ console.log('minutes' , minutes)
|
|
|
+ let mDeg = minutes * 6
|
|
|
+ let sec = seconds % 60
|
|
|
+ let sDeg = sec * 6
|
|
|
+
|
|
|
+ return (
|
|
|
+ <div className='container'>
|
|
|
+ <img src='./images/ClockFace_H.png' style={{ transform: `rotate(${hDeg}deg)` }} />
|
|
|
+ <img src='./images/ClockFace_M.png' style={{ transform: `rotate(${mDeg}deg)` }} />
|
|
|
+ <img src='./images/ClockFace_S.png' style={{ transform: `rotate(${sDeg}deg)` }} />
|
|
|
+ </div>
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
|
|
|
function App() {
|
|
|
-const[timer, setTimer] = useState()
|
|
|
+const[timer, setTimer] = useState({value: 0})
|
|
|
|
|
|
console.log('app timer state',timer)
|
|
|
|
|
@@ -330,7 +425,10 @@ console.log("проверка" , fCallbackTimer.current)
|
|
|
<Timer sec={50000}/>
|
|
|
<Timer2 sec={timer}/>
|
|
|
<TimerControl f={fCallbackTimer.current}/>
|
|
|
- </div>
|
|
|
+ <TimerContainer seconds={1800} refresh={100} render={SecondsTimer}/>
|
|
|
+ <TimerContainer seconds={18000} refresh={1000} render={TimerPresentation}/>
|
|
|
+ <TimerContainer seconds={3600000*5} refresh={1000} render={Watch}/>
|
|
|
+ </div>
|
|
|
);
|
|
|
}
|
|
|
|