UNPKG

calc-oa-lib

Version:

calc oa library

308 lines (271 loc) 8.11 kB
import PropTypes from "prop-types"; import React from "react"; import $calc from "../../utils/common"; import styleObj from "./index.module.scss"; export default class CalcCountDown extends React.Component { //#region 属性 & 构造函数 // 属性类型校验 static propTypes = { lv: PropTypes.number, customClass: PropTypes.string, beforeStartText: PropTypes.string, afterStartText: PropTypes.string, // 倒计时时间间隔, 单位s interval: PropTypes.number, // 是否自动启动倒计时 isAutoRunFirst: PropTypes.bool, // 是否执行启动事件当自动启动倒计时的时候 isExeStartEventWhenAutoRunFirst: PropTypes.bool, // 启动后执行的事件 onStartEvent: PropTypes.func, // 每帧需要执行的事件 // 假设显示器刷新频次为60HZ, 则一帧执行时间为1/60*1000 = 16.7ms onIntervalEvent: PropTypes.func, onFinishEvent: PropTypes.func, }; // 属性默认值 static defaultProps = { lv: 7, customClass: "custom-calc-count-down", beforeStartText: "Send Code", afterStartText: "Resend in {interval}", interval: 180, isAutoRunFirst: false, isExeStartEventWhenAutoRunFirst: false, onStartEvent: () => { console.log(`onStartEvent`); }, onIntervalEvent: (subTime) => { console.log(`onIntervalEvent`, subTime); }, onFinishEvent: () => { console.log(`onFinishEvent`); }, }; constructor(props) { super(props); this.state = { interval: this.props.interval, isCounting: false, }; } #data = { rafID: null, startTime: null, stopTime: null, subTime: null, }; //#endregion //#region 属性 // 显示文本 get showingText() { const _self = this; const { beforeStartText, afterStartText } = _self.props; const { interval, isCounting } = _self.state; let text = ""; if (!isCounting) { text = beforeStartText; } else { text = afterStartText.replace("{interval}", interval); } return text; } //#endregion //#region hooks render() { const _self = this; const { lv, customClass } = _self.props; const { isCounting } = _self.state; return ( <div className={`${styleObj["calc-count-down-scoped"]}`}> <div className={`calc-count-down ${customClass}`}> <div className={`label-btn ${isCounting ? "isCounting" : ""}`} onClick={(e) => { _self.#startCountDownEvent(true); }} > {_self.showingText} </div> </div> </div> ); } componentDidMount() { const _self = this; // 任务初始化 _self.initTask(() => {}); // console.log(`componentDidMount`); } shouldComponentUpdate(nextProps, nextState) { const _self = this; const removeProps = ["children"]; const nextPropsClone = $calc.tool.deepClone(nextProps, removeProps); const curPropsClone = $calc.tool.deepClone(_self.props, removeProps); const nextStateClone = $calc.tool.deepClone(nextState, removeProps); const curnextStateClone = $calc.tool.deepClone(_self.state, removeProps); const needRender = JSON.stringify(nextPropsClone) != JSON.stringify(curPropsClone) || JSON.stringify(nextStateClone) != JSON.stringify(curnextStateClone); // console.log("calc-count-down shouldComponentUpdate...", needRender); if (needRender) { return true; } return false; } componentDidUpdate(prevProps, prevState) {} componentWillUnmount() {} //#endregion //#region methods //任务初始化 //callBack: 回调函数 initTask(callBack) { const _self = this; // // 1>重置时间间隔 // _self.#resetInterval(callBack); const { isAutoRunFirst, isExeStartEventWhenAutoRunFirst = false } = _self.props; // console.log( // `initTask-isAutoRunFirst`, // isAutoRunFirst, // `initTask-isExeStartEventWhenAutoRunFirst`, // isExeStartEventWhenAutoRunFirst // ); isAutoRunFirst && _self.#startCountDownEvent(isExeStartEventWhenAutoRunFirst); callBack && callBack(); } //重置时间间隔 //callBack: 回调函数 #resetInterval(callBack) { const _self = this; const { interval } = _self.props; _self.setState( { interval, }, () => { callBack && callBack(); } ); } // 启动倒计时事件 // isExeStartEventWhenAutoRunFirst: 是否执行启动事件当自动启动倒计时的时候 #startCountDownEvent(isExeStartEvent = true) { const _self = this; // console.log(`startCountDownEvent-isExeStartEvent`, isExeStartEvent); _self.#start(isExeStartEvent); } // 启动倒计时 // isExeStartEvent: 是否执行启动事件, 默认true #start(isExeStartEvent = true) { const _self = this; // console.log(`start-isExeStartEvent`, isExeStartEvent); const { isCounting } = _self.state; const { interval, onStartEvent } = _self.props; // 若已经启动, 则返回 if (isCounting) { return false; } _self.setState( { isCounting: true, }, () => {} ); _self.#data.startTime = performance.now(); // const that = this; _self.#data.rafID = requestAnimationFrame((timestamp) => { _self.#step(timestamp); }); // 执行启动事件 isExeStartEvent && onStartEvent && onStartEvent(); } // 每帧需要执行的动作 #step(timestamp) { const _self = this; // console.log(timestamp); const { interval, onIntervalEvent, onFinishEvent } = _self.props; const { startTime } = _self.#data; _self.#data.subTime = timestamp - startTime; // 设置state.interval _self.#formatInterval(_self.#data.subTime); // 执行间隔事件 onIntervalEvent && onIntervalEvent(_self.#data.subTime); // 倒计时和时间到, 停止计数 if (Math.floor(_self.#data.subTime / 1000) >= interval) { _self.#finish(); return true; } // 继续执行下一帧动作 _self.#data.rafID = requestAnimationFrame((timestamp) => { _self.#step(timestamp); }); } // 格式化显示的间隔值 #formatInterval(subTime) { const _self = this; const { interval } = _self.props; subTime = $calc.tool.isNullOrEmpty(subTime) ? 0 : subTime; _self.setState( { interval: interval - Math.floor(subTime / 1000), }, () => {} ); } // 执行完毕 #finish() { const _self = this; const { interval, onFinishEvent } = _self.props; _self.#pause(); // 重置属性 _self.#data.rafID = null; _self.#data.startTime = null; _self.#data.stopTime = null; _self.#data.subTime = null; // 重置间隔时间 _self.#resetInterval(); // 执行完成事件 onFinishEvent && onFinishEvent(); } // 暂停 #pause() { const _self = this; const { isCounting } = _self.state; const { rafID } = _self.#data; // 若已经停止计数, 则pass if (!isCounting) { return; } _self.setState( { isCounting: false, }, () => {} ); _self.#data.stopTime = performance.now(); rafID && cancelAnimationFrame(rafID); } // 继续 #continue() { const _self = this; const { isCounting } = _self.state; let { startTime, stopTime } = _self.#data; // 若已经在计数, 则pass if (isCounting) { return; } _self.setState( { isCounting: true, }, () => {} ); _self.#data.startTime = startTime + performance.now() - stopTime; _self.#data.rafID = requestAnimationFrame((timestamp) => { _self.#step(timestamp); }); } //#endregion }