JS 倒计时展示小工具

lxf2023-03-15 16:54:01

废话开篇:JS 通过操控标签的隐藏与显示来实现倒计时展示小工具

一、实现效果

1、默认展示数字

JS 倒计时展示小工具

2、数字倒计时

JS 倒计时展示小工具

3、数字倒计时展示虚位

JS 倒计时展示小工具

4、数字倒计时不展示虚位

JS 倒计时展示小工具

5、小数

JS 倒计时展示小工具

6、负数

JS 倒计时展示小工具

7、小数倒计时

JS 倒计时展示小工具

每一个单独数字都是由一个独立的标签进行的绘制,再通过数字的处理将独立数字组合成一个完整的数字展示。

二、代码

1、HTML
<div class="numer_wsl_contain">
    <div id="number1"></div>
    <div id="number2"></div>
    <div id="number3"></div>
    <div id="number4"></div>
</div>

<script>
    let wholeNumberManage1 = new WholeNumberManage(21921,document.getElementById('number1'),1000)
    
    let wholeNumberManage2 = new WholeNumberManage(21921,document.getElementById('number2'),1000)
    wholeNumberManage2.isCountDown = true
    
    let wholeNumberManage3 = new WholeNumberManage(1021,document.getElementById('number3'),50)
    wholeNumberManage3.isCountDown = true
    
    let wholeNumberManage4 = new WholeNumberManage(1021,document.getElementById('number4'),50)
    wholeNumberManage4.isCountDown = true
    wholeNumberManage4.isShowVirtualNum = false
</script>

大部分的布局操作全部放到 JS 里,简化 html 内容。

2、CSS
.numer_wsl_contain{
    width: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

.number_wsl {
    margin-top: 50px;
    width: 100%;
    position: relative;
    display: flex;
    flex-direction: row;
    justify-content: center;
    align-items: center;
}

.num_wsl {
    position: relative;
    width: 23px;
    height: 30px;
}

/* 绘制横杠 */
.num_wsl_cross {
    position: absolute;
    left: 0px;
    width: 100%;
    height: 10%;
    background:
    linear-gradient(135deg,transparent 0, transparent 50%,black 50%,black 100%),
    linear-gradient(45deg,transparent 0, transparent 50%,black 50%,black 100%),
    linear-gradient(-135deg,transparent 0, transparent 50%,black 50%,black 100%),
    linear-gradient(-45deg,transparent 0, transparent 50%,black 50%,black 100%);
    background-repeat: no-repeat;
    background-position:
    0% 0%,
    0% 100%,
    100% 0%,
    100% 100%;
    background-size:
    50% 50%,
    50% 50%,
    50% 50%,
    50% 50%;
}

/* 3个横杠位置区分 */
.num_wsl_cross_one {
    top: 0px;
}

.num_wsl_cross_two {
    top: 45%;
}

.num_wsl_cross_three {
    top: 90%;
}

/* 绘制竖杠 */
.num_wsl_vertical {
    position: absolute;
    top: -5%;
    width: 12%;
    height: 65%;
    background:
    linear-gradient(135deg,transparent 0, transparent 50%,black 50%,black 100%),
    linear-gradient(45deg,transparent 0, transparent 50%,black 50%,black 100%),
    linear-gradient(-135deg,transparent 0, transparent 50%,black 50%,black 100%),
    linear-gradient(-45deg,transparent 0, transparent 50%,black 50%,black 100%);
    background-repeat: no-repeat;
    background-position:
    0% 0%,
    0% 100%,
    100% 0%,
    100% 100%;
    background-size:
    50% 50%,
    50% 50%,
    50% 50%,
    50% 50%;
}

/* 6个绘制竖杠位置区分 */
.num_wsl_vertical_one {
    left: 16.2%;
}

.num_wsl_vertical_two {
    left: 72.8%;
}

.num_wsl_vertical_three {
    top: 40.8%;
    left: 16.2%;
}

.num_wsl_vertical_four {
    top: 40.8%;
    left: 72.8%;
}

/* 小数点 */
.num_wsl_point {
    position: absolute;
    left: 35%;
    top: calc(90% - 7.5% * (23 / 30));
    width: 15%;
    height: calc(15% * (23 / 30));
    background-color: black;
}
3、数字管理类

NumberSingleManage 单数字展示类

// 单数字操作
class NumberSingleManage {
    // 操作dom对象
    el = null
    map = {}
    // 当前数字
    _num = 0

    constructor(num,el){
        if(el){
            this.el = el
        } else {
            // 没有dom传入对象,进行创建
            this.el = this.createElement(['num_wsl'])
        }
        // 创建单数字节点(3横,4竖)
        this.createSingleNumNode()
        this.getSingleMapRelationship()
        this.overwriteSetMethod()
        this.num = num
    }

    // 重写set方法
    overwriteSetMethod(){
        Object.defineProperty(this, 'num', {
            get: function () {
                return this._num
            },
            set: function (newValue) {
                this._num = newValue
                this.changeNum(this._num)
            }
        })
    }

    // 创建单数字节点
    createSingleNumNode(){
        //样式集合
        let classes = [
            ['num_wsl_cross','num_wsl_cross_one'],
            ['num_wsl_cross','num_wsl_cross_two'],
            ['num_wsl_cross','num_wsl_cross_three'],
            ['num_wsl_vertical','num_wsl_vertical_one'],
            ['num_wsl_vertical','num_wsl_vertical_two'],
            ['num_wsl_vertical','num_wsl_vertical_three'],
            ['num_wsl_vertical','num_wsl_vertical_four'],
            ['num_wsl_point']
        ]
        //添加节点
        classes.forEach(classNames => {
            this.el.appendChild(this.createElement(classNames))
        });
    }

    // 创建el并赋予样式
    createElement(classNames){
        let div = document.createElement('div')
            classNames.forEach((className)=>{
            div.classList.add(className)
        })
        return div
    }

    // 建立映射关系(数字对应数字节点的隐藏与显示)
    getSingleMapRelationship(){
        let num_cross_one = this.el.getElementsByClassName('num_wsl_cross_one')[0]
        let num_cross_two = this.el.getElementsByClassName('num_wsl_cross_two')[0]
        let num_cross_three = this.el.getElementsByClassName('num_wsl_cross_three')[0]
        let num_vertical_one = this.el.getElementsByClassName('num_wsl_vertical_one')[0]
        let num_vertical_two = this.el.getElementsByClassName('num_wsl_vertical_two')[0]
        let num_vertical_three = this.el.getElementsByClassName('num_wsl_vertical_three')[0]
        let num_vertical_four = this.el.getElementsByClassName('num_wsl_vertical_four')[0]
        this.map = {
            reset:()=>{
                this.elHide(num_wsl_point)
                this.elShow(num_cross_one)
                this.elShow(num_cross_two)
                this.elShow(num_cross_three)
                this.elShow(num_vertical_one)
                this.elShow(num_vertical_two)
                this.elShow(num_vertical_three)
                this.elShow(num_vertical_four)
            },
            allHide:()=>{
                this.elHide(num_wsl_point)
                this.elHide(num_cross_one)
                this.elHide(num_cross_two)
                this.elHide(num_cross_three)
                this.elHide(num_vertical_one)
                this.elHide(num_vertical_two)
                this.elHide(num_vertical_three)
                this.elHide(num_vertical_four)
                },
            '-':()=>{
                this.elHide(num_wsl_point)
                this.elHide(num_cross_one)
                this.elShow(num_cross_two)
                this.elHide(num_cross_three)
                this.elHide(num_vertical_one)
                this.elHide(num_vertical_two)
                this.elHide(num_vertical_three)
                this.elHide(num_vertical_four)
            },
            '.':()=>{
                this.elShow(num_wsl_point)
                this.elHide(num_cross_one)
                this.elHide(num_cross_two)
                this.elHide(num_cross_three)
                this.elHide(num_vertical_one)
                this.elHide(num_vertical_two)
                this.elHide(num_vertical_three)
                this.elHide(num_vertical_four)
            },
            0:()=>{
                this.elHide(num_wsl_point)
                this.elShow(num_cross_one)
                this.elHide(num_cross_two)
                this.elShow(num_cross_three)
                this.elShow(num_vertical_one)
                this.elShow(num_vertical_two)
                this.elShow(num_vertical_three)
                this.elShow(num_vertical_four)
            },
            1:()=>{
                this.elHide(num_wsl_point)
                this.elHide(num_cross_one)
                this.elHide(num_cross_two)
                this.elHide(num_cross_three)
                this.elHide(num_vertical_one)
                this.elShow(num_vertical_two)
                this.elHide(num_vertical_three)
                this.elShow(num_vertical_four)
            },
            2:()=>{
                this.elHide(num_wsl_point)
                this.elShow(num_cross_one)
                this.elShow(num_cross_two)
                this.elShow(num_cross_three)
                this.elHide(num_vertical_one)
                this.elShow(num_vertical_two)
                this.elShow(num_vertical_three)
                this.elHide(num_vertical_four)
            },
            3:()=>{
                this.elHide(num_wsl_point)
                this.elShow(num_cross_one)
                this.elShow(num_cross_two)
                this.elShow(num_cross_three)
                this.elHide(num_vertical_one)
                this.elShow(num_vertical_two)
                this.elHide(num_vertical_three)
                this.elShow(num_vertical_four)
            },
            4:()=>{
                this.elHide(num_wsl_point)
                this.elHide(num_cross_one)
                this.elShow(num_cross_two)
                this.elHide(num_cross_three)
                this.elShow(num_vertical_one)
                this.elShow(num_vertical_two)
                this.elHide(num_vertical_three)
                this.elShow(num_vertical_four)
            },
            5:()=>{
                this.elHide(num_wsl_point)
                this.elShow(num_cross_one)
                this.elShow(num_cross_two)
                this.elShow(num_cross_three)
                this.elShow(num_vertical_one)
                this.elHide(num_vertical_two)
                this.elHide(num_vertical_three)
                this.elShow(num_vertical_four)
            },
            6:()=>{
                this.elHide(num_wsl_point)
                this.elShow(num_cross_one)
                this.elShow(num_cross_two)
                this.elShow(num_cross_three)
                this.elShow(num_vertical_one)
                this.elHide(num_vertical_two)
                this.elShow(num_vertical_three)
                this.elShow(num_vertical_four)
            },
            7:()=>{
                this.elHide(num_wsl_point)
                this.elShow(num_cross_one)
                this.elHide(num_cross_two)
                this.elHide(num_cross_three)
                this.elHide(num_vertical_one)
                this.elShow(num_vertical_two)
                this.elHide(num_vertical_three)
                this.elShow(num_vertical_four)
            },
            8:()=>{
                this.elHide(num_wsl_point)
                this.elShow(num_cross_one)
                this.elShow(num_cross_two)
                this.elShow(num_cross_three)
                this.elShow(num_vertical_one)
                this.elShow(num_vertical_two)
                this.elShow(num_vertical_three)
                this.elShow(num_vertical_four)
            },
            9:()=>{
                this.elHide(num_wsl_point)
                this.elShow(num_cross_one)
                this.elShow(num_cross_two)
                this.elShow(num_cross_three)
                this.elShow(num_vertical_one)
                this.elShow(num_vertical_two)
                this.elHide(num_vertical_three)
                this.elShow(num_vertical_four)
            }
        }
    }

    //显示全部节点
    showAllNode(){
        this.map['reset']()
    }
    
    //隐藏全部节点
    hideAllNodel(){
        this.map['allHide']()
    }

    //变更数字
    changeNum(num){
        this.map[num] && this.map[num]()
    }

    //显示
    elShow(el){
        el.style.visibility="visible"
    }

    //隐藏
    elHide(el){
        el.style.visibility="hidden"
    }
}

WholeNumberManage 完整数字展示与操作类

// 整串数字操作
class WholeNumberManage {
    //单数字存储 
    numberSingleManages = [] 
    //完整数字 wholeNum = null 
    //展示的dom对象 mountedEl = null
    //定时器 timer = null 
    //是否显示虚位
    isShowVirtualNum = true
    //是否开始倒计时 
    _isCountDown = null 
    //倒计时时间间隔 
    _countDownInterval = 1000
    //初始整数位数
    _integerNumberLength = null
    
    constructor(wholeNum,el,countDownInterval){
        this.mountedEl = el
        this.mountedEl.classList.add("number_wsl")
        this.wholeNum = wholeNum
        this._countDownInterval = countDownInterval
        this.createWholeNumShowEl(this.parseWholeNum(wholeNum))
        this.overwriteSetMethod()
    }

    // 重写set方法
    overwriteSetMethod(){
        // 是否进行倒计时
        Object.defineProperty(this, 'isCountDown', {
            get: function () {
                return this._isCountDown
            },
            set: function (newValue) {
                if(newValue){
                    this._isCountDown = newValue
                    this.startCountDown()
                } else {
                    clearInterval(this.timer)
                }
            }
        })
    }

    // 倒计时
    startCountDown(){
        if(!this.wholeNum || this.wholeNum <= 0){
            return
        }
        if(this.timer){
            clearInterval(this.timer)
        }
        this.timer = setInterval(()=>{
            if(this.wholeNum < 0){
                clearInterval(this.timer)
            }
            this.wholeNum = math.subtract(math.bignumber(this.wholeNum),math.bignumber(1)).toString()
            this.countDownRefreshNum(this.parseWholeNum(this.wholeNum))
        },this._countDownInterval)
    }

    // 解析数字
    parseWholeNum(wholeNum){
        //获取整数部分长度
        this.setInitialIntegerNumberLength(wholeNum)
        //解析数字
        if(String(wholeNum).indexOf('.') == -1){
            return this.parseWholeIntegerlNum(wholeNum)
        } else {
            return this.parseWholeFloatNum(wholeNum)
        }
    }

    //设置初始整数位数
    setInitialIntegerNumberLength(wholeNum){
        if(!this._integerNumberLength){
            this._integerNumberLength = this.getIntegerLength(wholeNum)
        }
    }

    //获取整数位数
    getIntegerLength(wholeNum){
        return String(Math.abs(wholeNum)).split('.')[0].length
    }

    // 浮点型数字解析
    parseWholeFloatNum(wholeNum){
        let nums = []
        let numsByPoint = String(wholeNum).split('.')
        numsByPoint.forEach((num,index)=>{
            nums = nums.concat([...String(num)])
            if(index==0 && this.isShowVirtualNum){
                nums = this.complementInteger(nums,this._integerNumberLength - num.length)
            }
            nums.push('.')
        })
        nums.splice(-1)
        return nums
    }

    // 补位
    complementInteger(nums,complementLength){
        if(complementLength > 0){
            let zeroArr = new Array(complementLength)
            zeroArr.fill(0,complementLength,0)
            nums = [...zeroArr,...nums]
        }
        return nums
    }

    // 整型数字解析
    parseWholeIntegerlNum(wholeNum){
        let nums = [...String(wholeNum)]
        if(this.isShowVirtualNum){
            let currentIntegerNumberLength = this.getIntegerLength(wholeNum)
            let integerNumberLengthDifference = (this._integerNumberLength - currentIntegerNumberLength)
            nums = this.complementInteger(nums,integerNumberLengthDifference)
        }
        return nums
    }

    // 创建数字展示区
    createWholeNumShowEl(nums){
        nums.forEach((num)=>{
            this.numberSingleManages.push(new NumberSingleManage(num))
            if(this.mountedEl){
                this.mountedEl.appendChild(this.numberSingleManages.slice(-1)[0].el)
            }
        })
    }

    // 刷新数字
    countDownRefreshNum(nums){
        //删除多余的dom
        let virtualNum = (this.numberSingleManages.length - nums.length)
        if(virtualNum){
            let needMoveNumberSingleManages = this.numberSingleManages.splice(0,virtualNum)
            needMoveNumberSingleManages.forEach((numberSingleManages)=>{
                numberSingleManages.el.remove()
            })
         }
        //重置所有数据位0
        this.numberSingleManages.forEach((numberSingleManage)=>{
            numberSingleManage.num = 0
        })
        //进行数据填充
        nums.forEach((num,index)=>{
            if(index < this.numberSingleManages.length){
                let numberSingleManage = this.numberSingleManages[index]
                numberSingleManage.num = num
            } else {
                this.numberSingleManages.push(new NumberSingleManage(num))
                this.mountedEl.appendChild(this.numberSingleManages.slice(-1)[0].el)
            }
        })
    }
}

三、总结与思考

其实就是单纯的dom操作,没有特别难理解的内容。代码拙劣,大神勿笑[抱拳][抱拳][抱拳]