我如何使用React-hooks创建动画计时器
基本上我在尝试以动画环的形式显示剩余时间的进度。但是我以某种方式失败了
我刚刚跟随这个博客创建了动画计时器 https://css-tricks.com/how-to-create-an-animated-countdown-timer-with-html-css-and-javascript/
function setRemainingPathColor(timeLeft) {
const { alert, warning, info } = COLOR_CODES;
console.log(dataFromDiv);
if (timeLeft <= alert.threshold) {
dataFromDiv.current
.querySelectorAll("base-timer-path-remaining")
.classList.remove(warning.color);
dataFromDiv.current
.querySelectorAll("base-timer-path-remaining")
.classList.add(alert.color);
} else if (timeLeft <= warning.threshold) {
dataFromDiv.current
.querySelectorAll("base-timer-path-remaining")
.classList.remove(info.color);
dataFromDiv.current
.querySelectorAll("base-timer-path-remaining")
.classList.add(warning.color);
}
}
React.useEffect(() => {
let timer;
let timePassed = 0;
let timeLeft;
timer = counter > 0 && setTimeout(() => setCounter(counter - 1), 1000);
timePassed = timePassed += 1;
timeLeft = counter - timePassed;
setRemainingPathColor(timeLeft);
return () => {
if (timer) {
clearTimeout(timer);
}
};
}, [counter]);
您得到的错误是因为dataFromDiv.current.querySelectorAll(...)
总是返回undefined,因为要dataFromDiv.current
对其进行引用div#base-timer-path-remaining
是要修改的元素。因此,只需删除即可使您的代码正常工作.querySelectorAll(...)
。
但是,有一些更好的方法来组织代码:
代替直接进行dom操作,在这种情况下,更容易找出要使用哪种颜色useMemo
基于计数器值设置派生数据。
您还可以使用间隔而不是计时器,因为它更易于使用并且更清洁。这也使用setCounter的updater函数形式,因此不需要counter
在依赖项中具有效果。
我还在下面的示例中添加了一个重置按钮,因此您不必每次都重新运行它。
const pathColor = React.useMemo(() => {
const { alert, warning, info } = COLOR_CODES;
if (counter <= alert.threshold) {
return alert.color;
} else if (counter <= warning.threshold) {
return warning.color;
} else {
return info.color;
}
}, [counter]);
React.useEffect(() => {
const timerId = setInterval(() => {
setCounter(counter => {
if (counter <= 0) {
clearInterval(timerId);
return counter;
}
return counter - 1;
});
}, 1000);
return () => {
clearInterval(timerId);
};
}, [timerReset]); // this timerReset is to make sure that the interval starts off again whenever the reset button is pressed.
这条线只是强制重新渲染的一种方法。每当调用调度(重命名为)时,reducer函数x=>x+1
都会增加该timerReset
值resetTimer
。然后我使用timerReset
强制效果重新运行以重新开始间隔(如果它已停止)
const [timerReset, resetTimer] = React.useReducer(x => x + 1, 0);
const padTime = time => {
return String(time).length === 1 ? `0${time}` : `${time}`;
};
const format = time => {
const minutes = Math.floor(time / 60);
const seconds = time % 60;
return `${minutes}:${padTime(seconds)}`;
};
const WARNING_THRESHOLD = 10;
const ALERT_THRESHOLD = 5;
const COLOR_CODES = {
info: {
color: "green"
},
warning: {
color: "orange",
threshold: WARNING_THRESHOLD
},
alert: {
color: "red",
threshold: ALERT_THRESHOLD
}
};
function App() {
const [counter, setCounter] = React.useState(20);
const [timerReset, resetTimer] = React.useReducer(x => x + 1, 0);
const pathColor = React.useMemo(() => {
const { alert, warning, info } = COLOR_CODES;
if (counter <= alert.threshold) {
return alert.color;
} else if (counter <= warning.threshold) {
return warning.color;
} else {
return info.color;
}
}, [counter]);
React.useEffect(() => {
const timerId = setInterval(() => {
setCounter(counter => {
if (counter <= 0) {
clearInterval(timerId);
return counter;
}
return counter - 1;
});
}, 1000);
return () => {
clearInterval(timerId);
};
}, [timerReset]);
return (
<div className="App">
<div className="base-timer">
<svg
className="base-timer__svg"
viewBox="0 0 100 100"
xmlns="http://www.w3.org/2000/svg"
>
<g className="base-timer__circle">
<circle
className="base-timer__path-elapsed"
cx="50"
cy="50"
r="45"
/>
<path
id="base-timer-path-remaining"
className={`base-timer__path-remaining ${pathColor}`}
d="
M 50, 50
m -45, 0
a 45,45 0 1,0 90,0
a 45,45 0 1,0 -90,0
"
/>
</g>
</svg>
<span id="base-timer-label" className="base-timer__label">
{format(counter)}
</span>
</div>
<button
onClick={() => {
setCounter(20);
resetTimer();
}}
>
reset timer
</button>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
/* Sets the containers height and width */
.base-timer {
position: relative;
height: 300px;
width: 300px;
}
/* Removes SVG styling that would hide the time label */
.base-timer__circle {
fill: none;
stroke: none;
}
/* The SVG path that displays the timer's progress */
.base-timer__path-elapsed {
stroke-width: 7px;
stroke: grey;
}
.base-timer__path-remaining {
stroke-width: 7px;
stroke-linecap: round;
transform: rotate(90deg);
transform-origin: center;
transition: 1s linear all;
fill-rule: nonzero;
stroke: currentColor;
}
.base-timer__path-remaining.green {
color: rgb(65, 184, 131);
}
.base-timer__path-remaining.orange {
color: orange;
}
.base-timer__path-remaining.red {
color: red;
}
.base-timer__label {
position: absolute;
width: 300px;
height: 300px;
top: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 48px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句