react hooks 封装倒计时

setTimeout函数中执行代码的时间肯定是要大于等于setTimeout时间的,那么就可能出现设定的 1 秒,实际执行却执行了 2 秒的情况,那么我们的实现思路也很简单,每次计算一下setTimeout实际执行的时间,然后动态的调整下一次执行的时间,而不是设置固定的值。
我们来用图表举例推演一下每次执行的情况:
在这里插入图片描述
从中可以看到:下次执行的时间 nextTime = 1000 - totleTime % 1000;这样我们就可以得出下次执行的时间,从而每次都去动态的调整多余消耗的时间,大大减小倒计时最终的误差

还有需要考虑的是,实际业务中返回的剩余时间肯定不会是整数,所以我们的第一次执行的时间最好可以先让剩余时间变为整数,这样可以在倒计时到最后一秒时更加的精确。

根据上述的思路来看一下最终封装出来的 react hooks:

const useCountDown = ({ leftTime, ms = 1000, onEnd }) => {
    const countdownTimer = useRef();
    const startTimer = useRef();
    //记录初始时间
    const startTimeRef = useRef(performance.now());
    // 第一次执行的时间处理,让下一次倒计时时调整为整数
    const nextTimeRef = useRef(leftTime % ms);

    const [count, setCount] = useState(leftTime);

    const clearTimer = () => {
        countdownTimer.current && clearTimeout(countdownTimer.current);
        startTimer.current && clearTimeout(startTimer.current);
    };

    const startCountDown = () => {
        clearTimer();
        const currentTime = performance.now();
        // 算出每次实际执行的时间
        const executionTime = currentTime - startTimeRef.current;

        // 实际执行时间大于上一次需要执行的时间,说明执行时间多了,否则需要补上差的时间
        const diffTime =
            executionTime > nextTimeRef.current
                ? executionTime - nextTimeRef.current
                : nextTimeRef.current - executionTime;

        setCount((count) => {
            const nextCount =
                count - (Math.floor(executionTime / ms) || 1) * ms - nt;
            return nextCount <= 0 ? 0 : nextCount;
        });

        // 算出下一次的时间
        nextTimeRef.current =
            executionTime > nextTimeRef.current ? ms - diffTime : ms + diffTime;

        // 重置初始时间
        startTimeRef.current = performance.now();

        countdownTimer.current = setTimeout(() => {
            requestAnimationFrame(startCountDown);
        }, nextTimeRef.current);
    };

    useEffect(() => {
        setCount(leftTime);
        startTimer.current = setTimeout(startCountDown, nextTimeRef.current);
        return () => {
		clearTimer();
        };
    }, [leftTime]);

    useEffect(() => {
        if (count <= 0) {
            clearTimer();
            onEnd && onEnd();
        }
    }, [count]);

    return count;
};

export default useCountDown;
  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值