js-动手写一个简单的倒计时功能

606 阅读1分钟

之前做的电商项目,前端项目是用VUE开发的,基于VUE实现了倒计时组件。最近有个项目是REACT开发的,也需要用到倒计时组件,由于使用的是不同的框架,组件无法复用,所以特意提供一个实现了倒计时功能的函数,方便在此函数的基础上封装组件, 也在此做下记录, 方便下次查阅。直接上代码:

const checkParams = (startDate, endDate, format) => {
    if (!startDate) {
        throw new Error('startDate is not be null');
    }
    // 开始日期数据类型
    const startType = Object.prototype.toString.call(startDate).slice(8, -1);
    const allowType = ['String', 'Date'];
    if (!allowType.includes(startType)) {
        throw new Error('startDate type is error, allow String or Date');
    }
    // 开始日期数据类型
    const endType = Object.prototype.toString.call(endDate).slice(8, -1);
    if (!allowType.includes(endType)) {
        throw new Error('endDate is error, allow String or Date');
    }

};

const stringToDate = (date) =>{
    if (typeof date === 'string') {
        // 日期字符串转换为日期对象,并兼容ios
        return new Date(Date.parse(date.replace(/-/g, '/')));
    }
};

/**
 * 单个数字格式化为两位数, 9 => 09
 * @param num
 * @returns {string}
 */
const formatTwoDigit = (num) => {
    if (num < 10) {
        return (Array(2).join('0') + num).slice(-2);
    }
    return num;
};

/**
 * 倒计时
 * @param params - 参数, 包含以下属性:
 * startDate - 开始时间, 默认当前日期, 支持日期格式的字符串以及date对象
 * endDate - 结束时间, 支持日期格式的字符串以及date对象
 * format - 格式化, 默认hh:mm:ss, dd - 天, hh - 时, mm - 分, ss - 秒
 * cb(isEnd, result, hh, mm, ss) - 回调函数, isEnd - 是否结束, result - 根据format格式化后的结果, 后续参数是根据format返回相应的数值
 */
const countDown = (params = {}) => {
    let { startDate, endDate = new Date(), format = 'hh:mm:ss', cb = () => {} } = params;
    checkParams(startDate, endDate, format);

    let interval = null;

    startDate = stringToDate(startDate);
    endDate = stringToDate(endDate);

    const startTime = startDate.getTime(), endTime = endDate.getTime();
    if (isNaN(startTime)) {
        throw new Error('startDate not in date format');
    }
    if (isNaN(endTime)) {
        throw new Error('end not in date format');
    }

    // 获取相差的毫秒数
    let diffTime = endTime - startTime;

    if (diffTime > 0) {
        interval = setInterval(() => {
            if (diffTime <= 0) {
                clearInterval(interval);
                interval = null;
            }

            let m = formatTwoDigit(Math.floor(diffTime/1000/60%60));
            let s = formatTwoDigit(Math.floor(diffTime/1000%60));

            let result = '';

            // dd:hh:mm:ss
            if(/^(d{2}).+(h{2}).+(m{2}).+(s{2})$/.test(format)){
                let d = formatTwoDigit(Math.floor(diffTime/1000/60/60/24));
                let h = formatTwoDigit(Math.floor(diffTime/1000/60/60%24));
                result = format
                    .replace(/dd/, d)
                    .replace(/hh/, h)
                    .replace(/mm/, m)
                    .replace(/ss/, s);
                cb(diffTime <= 0, result, d, h, m, s);
            }
            // hh:mm:ss
            else if (/^(h{2}).+(m{2}).+(s{2})$/.test(format)) {
                let h = formatTwoDigit(Math.floor(diffTime/1000/60/60));
                result = format
                    .replace(/hh/, h)
                    .replace(/mm/, m)
                    .replace(/ss/, s);
                cb(diffTime <= 0, result,  h, m, s);
            }
            // mm:ss
            else if (/^(m{2}).+(s{2})$/.test(format)) {
                console.log(diffTime);
                m = formatTwoDigit(Math.floor(diffTime/1000/60));
                result = format
                    .replace(/mm/, m)
                    .replace(/ss/, s);
                cb(diffTime <= 0, result, m, s);
            } else {
                clearInterval(interval);
                interval = null;
                throw new Error('format is error');
            }
            diffTime -= 1000;
        }, 1000);
    }
};

测试代码:

countDown({
    startDate: '2020-09-21 17:59:50',
    endDate: '2020-09-21 18:00:00',
    cb: (isEnd, result) =>{
        console.log(isEnd, result);
    }
});