@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
202 lines (201 loc) • 6.83 kB
JavaScript
import { _ as __rest } from "./tslib.es6.js";
import React__default, { useState, useRef, useImperativeHandle, useEffect } from "react";
import { C as ComponentDefaults } from "./typings.js";
import { p as padZero } from "./pad-zero.js";
const defaultProps = Object.assign(Object.assign({}, ComponentDefaults), { paused: false, startTime: Date.now(), endTime: Date.now(), remainingTime: 0, millisecond: false, format: "HH:mm:ss", autoStart: true, time: 0, destroy: false });
const InternalCountDown = (props, ref) => {
const _a = Object.assign(Object.assign({}, defaultProps), props), { paused, startTime, endTime, remainingTime, millisecond, format, autoStart, time, destroy, className, style, onEnd, onPaused, onRestart, onUpdate, children } = _a, rest = __rest(_a, ["paused", "startTime", "endTime", "remainingTime", "millisecond", "format", "autoStart", "time", "destroy", "className", "style", "onEnd", "onPaused", "onRestart", "onUpdate", "children"]);
const classPrefix = "nut-countdown";
const [restTimeStamp, setRestTime] = useState(0);
const stateRef = useRef({
pauseTime: 0,
curr: 0,
isPaused: paused,
isIninted: false,
timer: 0,
restTime: 0,
// 倒计时剩余时间时间
counting: !paused && autoStart,
// 是否处于倒计时中
handleEndTime: Date.now(),
// 最终截止时间
diffTime: 0
// 设置了 startTime 时,与 date.now() 的差异
});
const getTimeStamp = (timeStr) => {
if (!timeStr)
return Date.now();
let t = timeStr;
t = Number(t) > 0 ? +t : t.toString().replace(/-/g, "/");
return new Date(t).getTime();
};
const initTime = () => {
if (remainingTime) {
stateRef.current.handleEndTime = Date.now() + Number(remainingTime);
} else {
stateRef.current.handleEndTime = endTime;
stateRef.current.diffTime = Date.now() - getTimeStamp(startTime);
}
if (!stateRef.current.counting)
stateRef.current.counting = true;
tick();
};
const tick = () => {
stateRef.current.timer = requestAnimationFrame(() => {
if (stateRef.current.counting) {
const currentTime = Date.now() - stateRef.current.diffTime;
const remainTime = Math.max(stateRef.current.handleEndTime - currentTime, 0);
stateRef.current.restTime = remainTime;
setRestTime(remainTime);
if (!remainTime) {
stateRef.current.counting = false;
pause();
onEnd && onEnd();
}
if (remainTime > 0) {
tick();
}
}
});
};
const formatRemainTime = (t, type) => {
const ts = t;
const rest2 = {
d: 0,
h: 0,
m: 0,
s: 0,
ms: 0
};
const SECOND = 1e3;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const DAY = 24 * HOUR;
if (ts > 0) {
rest2.d = ts >= SECOND ? Math.floor(ts / DAY) : 0;
rest2.h = Math.floor(ts % DAY / HOUR);
rest2.m = Math.floor(ts % HOUR / MINUTE);
rest2.s = Math.floor(ts % MINUTE / SECOND);
rest2.ms = Math.floor(ts % SECOND);
}
return type === "custom" ? rest2 : parseFormat(Object.assign({}, rest2));
};
const parseFormat = (time2) => {
const { d } = time2;
let { h, m, s, ms } = time2;
let formatCache = format;
if (formatCache.includes("DD")) {
formatCache = formatCache.replace("DD", padZero(d));
} else {
h += Number(d) * 24;
}
if (formatCache.includes("HH")) {
formatCache = formatCache.replace("HH", padZero(h));
} else {
m += Number(h) * 60;
}
if (formatCache.includes("mm")) {
formatCache = formatCache.replace("mm", padZero(m));
} else {
s += Number(m) * 60;
}
if (formatCache.includes("ss")) {
formatCache = formatCache.replace("ss", padZero(s));
} else {
ms += Number(s) * 1e3;
}
if (formatCache.includes("S")) {
const msC = padZero(ms, 3).toString();
if (formatCache.includes("SSS")) {
formatCache = formatCache.replace("SSS", msC);
} else if (formatCache.includes("SS")) {
formatCache = formatCache.replace("SS", msC.slice(0, 2));
} else if (formatCache.includes("S")) {
formatCache = formatCache.replace("SS", msC.slice(0, 1));
}
}
return formatCache;
};
const pause = () => {
cancelAnimationFrame(stateRef.current.timer);
stateRef.current.counting = false;
onPaused && onPaused(stateRef.current.restTime);
};
useImperativeHandle(ref, () => ({
start: () => {
if (!stateRef.current.counting && !autoStart) {
stateRef.current.counting = true;
stateRef.current.handleEndTime = Date.now() + Number(stateRef.current.restTime);
tick();
onRestart && onRestart(stateRef.current.restTime);
}
},
pause: () => {
cancelAnimationFrame(stateRef.current.timer);
stateRef.current.counting = false;
onPaused && onPaused(stateRef.current.restTime);
},
reset: () => {
if (!autoStart) {
pause();
stateRef.current.restTime = time;
setRestTime(time);
}
}
}));
useEffect(() => {
const tranTime = formatRemainTime(stateRef.current.restTime, "custom");
onUpdate && onUpdate(tranTime);
}, [restTimeStamp]);
useEffect(() => {
if (stateRef.current.isIninted) {
if (paused) {
if (stateRef.current.counting) {
pause();
}
} else {
if (!stateRef.current.counting) {
stateRef.current.counting = true;
stateRef.current.handleEndTime = Date.now() + Number(stateRef.current.restTime);
tick();
}
onRestart && onRestart(stateRef.current.restTime);
}
}
}, [paused]);
useEffect(() => {
if (stateRef.current.isIninted) {
initTime();
}
}, [endTime, startTime, remainingTime]);
useEffect(() => {
if (autoStart) {
initTime();
} else {
stateRef.current.restTime = time;
setRestTime(time);
}
if (!stateRef.current.isIninted) {
stateRef.current.isIninted = true;
}
return componentWillUnmount;
}, []);
const componentWillUnmount = () => {
destroy && cancelAnimationFrame(stateRef.current.timer);
};
const renderTime = (() => {
return formatRemainTime(stateRef.current.restTime);
})();
return React__default.createElement("div", Object.assign({ className: `${classPrefix} ${className}`, style: Object.assign({}, style) }, rest), children || React__default.createElement("div", {
className: `${classPrefix}-block`,
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML: {
__html: `${renderTime}`
}
}));
};
const CountDown = React__default.forwardRef(InternalCountDown);
CountDown.displayName = "NutCountDown";
export {
CountDown as default
};