为了账号安全,请及时绑定邮箱和手机立即绑定

set状态不会立即在设置内部更新状态

set状态不会立即在设置内部更新状态

FFIVE 2022-09-29 15:47:59
我正在尝试构建一个模拟时钟,秒针每秒旋转一次,最小指针每分钟旋转6度,小时指针每12分钟旋转6度。这是代码和框:https://codesandbox.io/s/react-example-d7mes?file=/Clock.js每当最小指针的角度为其中任何一个(12分钟度)时,我都会将时针旋转6度一次。[72, 144, 216, 288, 360]这就是我正在做的事情: 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)但是时针不会改变,并且第二次被设置回other部分中的上一个值,并且返回的值被忽略,因为在状态更新之前,称为下一秒,它返回的仍然是旧值(不是在incHrselseprev.hrsif(!minChanged && indx >=0))如何解决此问题?
查看完整描述

1 回答

?
沧海一幻觉

TA贡献1824条经验 获得超5个赞

这里有一个基本的设计问题,那就是设置间隔是保持时间的错误工具。 只保证回调不会运行至少1000毫秒,而不是保证它将在1000毫秒内完全运行,所以你最终会得到漂移和跳过的时间。setInterval

我建议使用请求动画帧日期库或性能来确定何时发生了刻度。

通过此设置,您可以使用以下命令将时针与小时剩余的分钟数成比例地缩放:

(hours + minutes / 60) * 30 + 180

如果您希望对时针调整进行更粗糙的粒度,请将分钟截断为 6 个不同的块:

(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>


查看完整回答
反对 回复 2022-09-29
  • 1 回答
  • 0 关注
  • 197 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号