• IOS系统定时APP


    将页面分为时间显示部分,控制部分,显示计次共三个部分。实现的功能有:启动定时器,计次,停止,复位。

    计算:当前显示的时间 = 当前计次的累积时间 + 已经结束的所有计次的累积时间和;

    关于 new Date().getTime() 实现,google准确,Firefox 误差很大;

    涉及到的时间计算,都是用 setInterval实现,没有用 new Date();

    尝试过setInterval 与 new Date两者混用,一是误差很大,二是逻辑不够强;

    经测试在google浏览器和IOS原组件的误差很小(毫秒级别),准确度可靠;Firefox 误差很大;

      1class Stopwatch {
    2    constructor(id) {
    3        this.container = document.getElementById(id);
    4        this.display = this.container.querySelector('.display');   // 时间显示
    5        this.lap = this.container.querySelector('.lap');           // 计次显示
    6
    7        // 计数相关变量
    8        this._stopwathchTimer = null;                              // 计时器
    9        this._count = 0;                                           // 计次的次数
    10        this._timeAccumulation = 0;                                // 累积时长
    11        this._timeAccumulationContainer = [];                      // 存放已经结束的计次的容器
    12        this._s = 0;                                               // 已经结束的所有计次累积时间
    13        this._stopwatchHandlers = [];                              // 用于tartTimer里回调的函数
    14
    15        // 控制流
    16        this.ctrl = this.container.querySelector('.ctrl');         // 控制部分
    17        if(this.ctrl) {
    18            let btns = this.ctrl.querySelectorAll('button');
    19            let startStopBtn = btns[1];                            // 开始和暂停按钮
    20            let lapResetBtn = btns[0];                             // 计次和复位按钮
    21
    22            // 样式更改
    23            let changeStyle = {                                   
    24                clickStart : function(){
    25                    lapResetBtn.disabled = '';                     // 计次按钮生效
    26                    startStopBtn.innerHTML = '停止';
    27                    startStopBtn.className = 'stop';
    28                    lapResetBtn.innerHTML = '计次';
    29                    lapResetBtn.className = 'active';
    30                },
    31                clickStop : function() {
    32                    startStopBtn.innerHTML = '启动';
    33                    startStopBtn.className = 'start';
    34                    lapResetBtn.innerHTML = '复位';
    35                },
    36                clickReset : function() {
    37                    lapResetBtn.disabled = 'disabled';             // 计次按钮失效
    38                    lapResetBtn.innerHTML = '计次';
    39                    lapResetBtn.className = '';
    40                    this.display.innerHTML = '00:00.00';
    41                    this.lap.innerHTML = ''
    42                }
    43            };
    44
    45            // 事件处理函数
    46            let eventHandler = {
    47                start: function() {
    48                    lapResetBtn.removeEventListener('click', resetBind);            // 移除复位事件;选择启动,就移除复位
    49                    console.log('启动');
    50                    changeStyle.clickStart.call(this);                              // 改变按钮显示样式
    51                    if(this._count === 0) {                                         // 如果首次启动计时器,增加一条计次    
    52                        this._count = 1
    53                        // console.log('开始事件中的计数次', this._count)
    54                        this.insertLap();                                           // 插入计次 
    55                    }       
    56                    this.startTimer();   
    57                    startStopBtn.removeEventListener ('click', startBind);          // 移除启动计时事件
    58                    lapResetBtn.addEventListener('click', lapfBind)                 // 添加计次事件                                                   
    59                    startStopBtn.addEventListener('click', stopBind)                // 添加停止计时事件
    60                },
    61
    62                stop: function() {
    63                    console.log('停止'); 
    64                    changeStyle.clickStop.call(this);                               // 改变按钮显示样式
    65                    this.stopTimer();                                               // 停止计时;
    66                    startStopBtn.removeEventListener('click', stopBind)             // 移除停止计时事件
    67                    startStopBtn.addEventListener('click', startBind);              // 重新添加启动计时事件
    68                    lapResetBtn.removeEventListener('click', lapfBind);             // 移除计次事件;
    69                    lapResetBtn.addEventListener('click', resetBind);               // 添加复位事件
    70                },
    71
    72                lapf: function() {                                                       
    73                    this.insertLap();                                               // 插入新计次
    74                    this._timeAccumulationContainer.push(this._timeAccumulation);   // 将当前结束的计次推入容器,保存起来
    75                    this._s += this._timeAccumulationContainer[this._count - 1];    // 累加已经结束的所有计次
    76                    console.log('计次''当前累积的计次时间'this._s);
    77                    this._timeAccumulation = 0;                                     // 计时器清零,这条放在求和后面!
    78                    this._count++;                                            
    79                },
    80
    81                reset: function() {                                                 // 复位事件
    82                    console.log('复位');
    83                    changeStyle.clickReset.call(this);                              // 改变按钮显示
    84                    // 重置
    85                    this._stopwathchTimer = null;                            
    86                    this._count = 0;                                          
    87                    this._timeAccumulation = 0;                              
    88                    this._timeAccumulationContainer = [];                     
    89                    this._s = 0
    90                    lapResetBtn.removeEventListener('click', resetBind);            // 复位是所有事件中最后绑定的用完应该删除
    91                }
    92            }
    93
    94            // 事件绑定
    95            // 事件函数副本
    96            let startBind = eventHandler.start.bind(this),                          // bind 每次会弄出新函数...
    97                stopBind = eventHandler.stop.bind(this),                     
    98                lapfBind = eventHandler.lapf.bind(this),
    99                resetBind = eventHandler.reset.bind(this);
    100            startStopBtn.addEventListener('click', startBind);
    101        }
    102
    103        // 用于监听startTimer
    104        this.addStopwatchListener(_timeAccumulation => {
    105            this.displayTotalTime(_timeAccumulation);
    106        })
    107        this.addStopwatchListener(_timeAccumulation => {
    108            this.displayLapTime(_timeAccumulation);
    109        })
    110    }
    111
    112    // API
    113    // 计时器
    114    startTimer() {
    115        this.stopTimer();
    116        this._stopwathchTimer = setInterval(() => {   
    117            this._timeAccumulation++;                          // 注意时间累积量 _timeAccumulation 是厘秒级别的(因为界面显示的是两位)
    118            this._stopwatchHandlers.forEach(handler => {       // 处理回调函数
    119                handler(this._timeAccumulation);
    120            })
    121        }, 1000 / 100)
    122    }
    123
    124    stopTimer() {
    125        clearInterval(this._stopwathchTimer );
    126    }
    127
    128    // 总时间显示(从启动到当前时刻的累积时间)
    129    displayTotalTime(_timeAccumulation) {
    130        let totaltimeAccumulation = this._timeAccumulation * 10  + this._s * 10;     // _s为_timeAccumulation累积时间队列之和;
    131        this.display.innerHTML = `${this.milSecond_to_time(totaltimeAccumulation)}`;
    132    }
    133    // 计次条目显示
    134    displayLapTime(_timeAccumulation) {
    135        let li = this.lap.querySelector('li'),
    136            spans = li.querySelectorAll('span'),
    137            task = spans[0], time = spans[1];
    138
    139        task.innerHTML = `计次${this._count}`;
    140        time.innerHTML = `${this.milSecond_to_time(this._timeAccumulation * 10)}`;
    141    }
    142
    143    // 插入一个计次
    144    insertLap() {
    145        let t = this.templateLap(); // 显示计次
    146        this.lap.insertAdjacentHTML('afterBegin', t);
    147    }
    148    // 计次内容模板
    149    templateLap() {
    150        let t = `
    151        <li><span></span><span></span></li>
    152        `
    153        return t;
    154    }
    155
    156    // 将时间累积量转化成时间
    157    milSecond_to_time(t) {                                         // t 时间间隔,单位 ms
    158        let time,
    159            minute = this.addZero(Math.floor(t / 60000) % 60),     // 分
    160            second = this.addZero(Math.floor(t / 1000) % 60),      // 秒
    161            centisecond = this.addZero(Math.floor(t / 10) % 100) ; // 厘秒(百分之一秒)
    162        time = `${minute}:${second}.${centisecond}`;
    163        return time;
    164    }
    165    // 修饰器;加零
    166    addZero(t) {
    167        t = t < 10 ? '0' + t : t; 
    168        return t;
    169    }
    170    // 添加监听startTimer的事件函数
    171    addStopwatchListener(handler) {
    172        this._stopwatchHandlers.push(handler);
    173    }
    174}
    175
    176// 调用
    177const stopwatch = new Stopwatch('stopwatch');

    一个200行的小demo,收获不少

    从基于实现组件功能开始,到使用class封装组件;

    最小化访问DOM元素;

    相关变量放在一起,将样式更改函数放在一块,将事件处理函数放在一块;

    绑定this(非箭头函数this丢失),bind的时候每次都会重新生成新函数(将函数bind后统一赋给一个变量,这样增加事件和删除事件所用的函数就是同一个了);

    增加事件监听器,统一管理需要调用函数变量的系列相关事件;

    将函数抽象到最纯(函数就是函数不与组件的元素相互耦合),使用Decorate(装饰器);

    由于在同一个按钮上绑定了不同的事件,因此事件绑定与移除的顺序很重要;

    https://rencoo.github.io/appDemo/iosStopwatch/index.html
    另外一篇文章, 用状态模式重构了这个小demo https://www.cnblogs.com/rencoo/p/10115341.html

  • 相关阅读:
    公布一些常用的WebServices
    ARM的嵌入式Linux应用程序开发研究
    c++读写剪贴板代码
    如何破解路由器密码(CISCO)!
    用Shell扩展实现源代码统计程序(转)
    图文例解C++类的多重继承与虚拟继承
    MSDN上关于ADO示例代码
    不错的句子
    codeforces #271(div2) F. Ant colony
    [Z]CUDA中Bank conflict冲突
  • 原文地址:https://www.cnblogs.com/rencoo/p/9484268.html
Copyright © 2020-2023  润新知