gpt4 book ai didi

javascript - setState 不会在 setInterval 内立即更新状态

转载 作者:行者123 更新时间:2023-12-01 17:16:40 27 4
gpt4 key购买 nike

我正在尝试构建一个模拟时钟,秒针每秒旋转一次,分针每分钟旋转 6 度,时针每 12 分钟旋转 6 度。

这是代码框:https://codesandbox.io/s/react-example-d7mes?file=/Clock.js

每当分针的 Angular 是这些[72, 144, 216, 288, 360](12 分度)中的任何一个时,我将时针旋转 6 度一次。

这就是我正在做的:

 let twelveMinDegrees = [72, 144, 216, 288, 360];

setInterval(() => {
this.setState(prev => ({
sec: prev.sec == 360 ? 6 : prev.sec + 6, //degrees
min: prev.sec == 354 ? (prev.min == 360 ? 6 : prev.min + 6) : prev.min, //degrees
hrs: (function(){ //degrees

const indx = twelveMinDegrees.findIndex(el => el == prev.min)
if(!minChanged && indx >=0){ //only change once for every 12min
minChanged = true;
let incHrs = prev.hrs + (6*indx);
console.log(incHrs);
return incHrs;
}else{
if(!twelveMinDegrees.includes(prev.min)){
minChanged = false;
}
return prev.hrs;
}
})()
}))
}, 1000)

但是时针没有改变,并且在 第二次 的 else 部分设置回之前的值并且返回的值 incHrs 被忽略,因为在状态之前更新后,下一秒调用 else 返回 prev.hrs ,它仍然是旧值(不是 if(!minChanged &&索引 >=0))

我该如何解决这个问题?

最佳答案

这里有一个基本的设计问题,即 setInterval是计时的错误工具。 setInterval 只保证回调不会运行至少 1000 毫秒,而不是它会正好 1000 毫秒运行,所以你'我们将以漂移和跳过的时间结束。

我推荐使用 requestAnimationFrameDate图书馆或 performance.now确定滴答声何时发生。

通过此设置,您可以按小时剩余分钟数的比例缩放时针:

(hours + minutes / 60) * 30 + 180

如果您想要更粗略地调整时针,请将分钟 chop 为 6 个不同的 block :

(hours + floor(minutes / 10) * 10 / 60) * 30 + 180

以数学方式执行此操作比在硬编码数组中查找增量点要简单得多。

这是一个您可以用来保持准确时间的最小示例(我会把样式留给您):

.hand {
width: 2px;
height: 40%;
background-color: black;
transform-origin: top center;
position: absolute;
border-radius: 3px;
top: 50%;
left: 50%;
}

.analog-clock {
position: relative;
border-radius: 50%;
border: 1px solid #aaa;
height: 120px;
width: 120px;
}
<script type="text/babel" defer>
const {Fragment, useEffect, useState, useRef} = React;

const Clock = () => {
const [date, setDate] = useState(new Date());
const requestRef = useRef();
let prevDate = null;

const tick = () => {
const now = new Date();

if (prevDate && now.getSeconds() !== prevDate.getSeconds()) {
setDate(now);
}

prevDate = now;
requestRef.current = requestAnimationFrame(tick);
};

useEffect(() => {
requestRef.current = requestAnimationFrame(tick);
return () => cancelAnimationFrame(requestRef.current);
}, []);

const pad = n => n.toString().padStart(2, 0);

const computeHourDeg = date =>
(date.getHours() + ~~(date.getMinutes() / 10) * 10 / 60) * 30 + 180
;

return (
<Fragment>
<div className="analog-clock">
<div
className="hand"
style={{transform: `rotate(${6 * date.getSeconds() + 180}deg)`}}
></div>
<div
className="hand"
style={{transform: `rotate(${6 * date.getMinutes() + 180}deg)`}}
></div>
<div
className="hand"
style={{background: "red",
height: "30%",
transform: `rotate(${computeHourDeg(date)}deg)`}}
></div>
</div>
<h3>
{pad(date.getHours())}:
{pad(date.getMinutes())}:
{pad(date.getSeconds())}
</h3>
</Fragment>
);
};

ReactDOM.render(<Clock />, document.body);

</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>

这是一个加速版本,其中包含模拟的 Date 对象,以说明其正常工作:

.hand {
width: 2px;
height: 40%;
background-color: black;
transform-origin: top center;
position: absolute;
border-radius: 3px;
top: 50%;
left: 50%;
}

.analog-clock {
position: relative;
border-radius: 50%;
border: 1px solid #aaa;
height: 120px;
width: 120px;
}
<script type="text/babel" defer>
const {Fragment, useEffect, useState, useRef} = React;

const speedMS = 5;

class MockDate {
static second = 0;
static minute = 0;
static hour = 0;

constructor() {
this.second = MockDate.second;
this.minute = MockDate.minute;
this.hour = MockDate.hour;
}

getSeconds() {
return this.second;
}

getMinutes() {
return this.minute;
}

getHours() {
return this.hour || 12;
}
}

setInterval(() => {
if (++MockDate.second === 60) {
MockDate.second = 0;

if (++MockDate.minute === 60) {
MockDate.minute = 0;
MockDate.hour = (MockDate.hour + 1) % 12;
}
}
}, speedMS);

const Clock = () => {
const [date, setDate] = useState(new MockDate());
const requestRef = useRef();
let prevDate = null;

const tick = () => {
const now = new MockDate();

if (prevDate && now.getSeconds() !== prevDate.getSeconds()) {
setDate(now);
}

prevDate = now;
requestRef.current = requestAnimationFrame(tick);
};

useEffect(() => {
requestRef.current = requestAnimationFrame(tick);
return () => cancelAnimationFrame(requestRef.current);
}, []);

const pad = n => n.toString().padStart(2, 0);

const computeHourDeg = date =>
(date.getHours() + ~~(date.getMinutes() / 10) * 10 / 60) * 30 + 180
;

return (
<Fragment>
<div className="analog-clock">
<div
className="hand"
style={{transform: `rotate(${6 * date.getSeconds() + 180}deg)`}}
></div>
<div
className="hand"
style={{transform: `rotate(${6 * date.getMinutes() + 180}deg)`}}
></div>
<div
className="hand"
style={{background: "red",
height: "30%",
transform: `rotate(${computeHourDeg(date)}deg)`}}
></div>
</div>
<h3>
{pad(date.getHours())}:
{pad(date.getMinutes())}:
{pad(date.getSeconds())}
</h3>
</Fragment>
);
};


ReactDOM.render(<Clock />, document.body);

</script>

<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

关于javascript - setState 不会在 setInterval 内立即更新状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62328108/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com