const { Component, useState } = React;
class CountDown extends Component {
timer = null;
state = {
status: "pending",
endTime: 0,
progress: 0,
};
start() {
this.execute("onStart");
this.setState({ status: "running" });
const { diff, format } = this.props;
const running = () => {
const now = Date.now();
const progress = Math.max(this.state.endTime - now, 0);
let _progress = "";
if (typeof format === "string") {
_progress = this.formatTime(progress, format);
} else if (typeof format === "function") {
_progress = format.call(null, progress);
}
this.setState({ progress: _progress });
this.execute("onStep", _progress);
if (progress === 0) {
this.stop();
}
return progress;
};
if (diff && diff >= 17) {
this.timer = setInterval(() => {
running();
}, diff);
} else {
const requestAnimFrame =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 1000 / 60);
};
setTimeout(() => {
(function loop() {
const progress = running();
if (progress > 0) {
requestAnimFrame(loop);
}
})();
}, 0);
}
}
stop() {
clearInterval(this.timer);
this.setState({ status: "ended" });
this.execute("onEnd");
}
componentDidMount() {
const { endTime, total } = this.props;
if (!endTime && !total) {
console.error(`endTime and total need least one`);
} else {
const now = Date.now();
let _endTime = 0;
if (!endTime) {
_endTime = now + (total || 0);
} else {
_endTime = endTime;
}
this.setState({ endTime: _endTime });
this.start();
}
}
formatTime(timestamp, format = "dd hh:mm:ss.ii") {
const dateFormat = {
"d+": Math.floor(timestamp / 1000 / 60 / 60 / 24),
"h+": Math.floor((timestamp / 1000 / 60 / 60) % 24),
"m+": Math.floor((timestamp / 1000 / 60) % 60),
"s+": Math.floor((timestamp / 1000) % 60),
"i+": timestamp % 1000,
};
for (let key in dateFormat) {
if (new RegExp("(" + key + ")").test(format)) {
format = format.replace(RegExp.$1, () => {
const regLen = RegExp.$1.length;
if (/i+/.test(RegExp.$1)) {
return (dateFormat[key] + "000").substr(0, regLen);
}
return regLen === 1 ? dateFormat[key] : ("00" + dateFormat[key]).substr(("" + dateFormat[key]).length);
});
}
}
return format;
}
execute(fn, ...params) {
const func = this.props[fn];
if (typeof func === "function") {
func(...params);
}
}
render() {
return <p>{this.state.progress}</p>;
}
}
const SelfFormatCount = () => {
const [ended, setEnded] = useState(false);
return (
<div>
<CountDown
total={5000}
format={(timestamp) => {
return (timestamp / 1000).toFixed(3);
}}
onEnd={() => setEnded(true)}
/>
{ended && <p style={{ color: "#f00" }}>已结束</p>}
</div>
);
};
ReactDOM.render(
<div>
<div class="item">
<p>10秒钟的倒计时:</p>
<CountDown total={10000} format={"mm:ss.iii"} />
</div>
<div class="item">
<p>自定义格式的10秒钟的倒计时:</p>
<SelfFormatCount />
</div>
<div class="item">
<p>距离早晨8点的倒计时:</p>
<CountDown endTime={new Date().setHours(8, 0, 0, 0)} format={"hh小时mm分ss秒"} />
</div>
<div class="item">
<p>距离中午12点30分的倒计时:</p>
<CountDown endTime={new Date().setHours(12, 30, 0, 0)} format={"hh小时mm分ss秒"} />
</div>
<div class="item">
<p>距离今晚24点的倒计时:</p>
<CountDown endTime={new Date().setHours(24, 0, 0, 0)} format={"hh小时mm分ss秒"} />
</div>
</div>,
document.getElementById("app")
);