zoukankan      html  css  js  c++  java
  • 【项目分析】设计一个计时器的jQuery插件(4)(附赠:中秋节祝福)

    背景

    目前团队所负责的公务员考试的项目中,需要使用到计数器的前端JS组件,具体应用可以查看页面

    1

    你会发现这里的计时器代码有些问题。

    这里使用的是

    代码
    this._timerId = window.setInterval(function() { 
                        $
    this._step(); 
                    }, 
    1000);

    //

    _step: 
    function() { 
               
    if (this._pattern == 'down'
                   
    this._currentTime -= 1
               
    else 
                   
    this._currentTime += 1
               
    //每秒执行一次回调函数 
               if (this._currentTime == 0 || this._currentTime > this._totalTime) { 
                   
    this.Stop(); 
               } 
               
    else { 
                   
    this._runCallBack(this._onInterval); 
               } 
           }

    可以发现,这里在触发1秒的时候,使用的是直接增加1秒或者减少1秒的方式进行。这样是不合理的。我们可以发现我们在浏览器上选择右键,这里的计时器会被停止,当你下次再触发事件的时候,时间就不准确了。这样,只要用户点击右键,就能够把计时器给停掉。有的人说,可以屏蔽右键,但是并不是所有浏览器,都能够支持屏蔽右键,并且还有其他的方式能够使计时器停止;或者,可以使用flash来实现的计时器的功能,嗯,这样是可以的,只是我们的开发人员对于flash不是很熟悉,于是想还是自己重新设计下这个JS的计时器组件。

    于是我这里采用了jQuery插件的方式来重写这个计时器组件。

    具体分析

    1. 首先先来看下最后的调用方法是什么样的:

    代码
    <div id="counter-1"> 
    </div>

    <div id="counter-2"> 
    </div>

    $('#counter-1').counter({ 'pattern': 'down', 'totalTime': 3600, 'onInterval': fnInterval, 'onStop': fnStop }); 
    $('#counter-2').counter({ 'pattern': 'up' });

    通过代码可以很直观的看到实现的方式,其中pattern是计时器的方式(累减或者累加),totalTime是倒计时总时间,onInterval是每跳动一次计时器,需要触发的事件,onStop是等计时器倒计时完毕后触发的事件。

    2. 然后,来看看jquery.counter.js中的核心代码:

    代码
    /* 主函数 */ 
    $.fn.counter 
    = function(options) { 
        
    var args = Array.prototype.slice.call(arguments, 1); 

        
    return this.each(function() { 
            
    if (typeof options == 'string') { 
                $.counter[
    '_' + options + 'Counter'].apply($.counter, [this].concat(args)); 
            } 
            
    else { 
                $.counter._attachCounter(
    this, options); 
            } 
        }); 
    };

    这里的options是Object类型的,所以调用 $.counter._attachCounter(this, options)。

    接着看:

    代码
    /* 绑定计数器 */ 
    _attachCounter: 
    function(target, options) { 
        
    var inst = { options: $.extend({ 'from'new Date() }, options) }; 
        $.data(target, DATA_NAME, inst); 
        
    this._changeCounter(target); 
    }

    这里把inst的选项数据作为一个数据对象在客户端保存起来 $.data(target, DATA_NAME, inst);

    /* 重置计数器 */ 
    _changeCounter: 
    function(target) { 
        
    this._addTarget(target); 
        
    this._updateCounter(target); 

    }

     其中_addTarget是为了把目标的jQuery对象放在一个目标计时器列表_timerTargets中去维护。

    代码
    /* 重新显示计数器 */ 
    _updateCounter: 
    function(target) { 

        
    var remainTime = this._getTime(target); 
        
    if (remainTime) { 
            
    //回调函数调用 
            var inst = $.data(target, DATA_NAME); 
            
    if (remainTime >= 0) { 
                
    var time = this._getFormatTime(remainTime); 
                $(target).html(time); 

                
    var onInterval = this._get(inst, 'onInterval'); 
                
    if (onInterval) { 
                    onInterval.apply(target, [remainTime]); 
                } 
            } 
            
    else { 
                remainTime 
    = 0
                
    var time = this._getFormatTime(remainTime); 
                $(target).html(time); 

                
    var onStop = this._get(inst, 'onStop'); 
                
    if (onStop) { 
                    onStop.apply(target, []); 
                    
    this._removeTarget(target); 
                } 
            } 
        }

    这里是前端显示的核心代码,onInterval,onStop会触发相关的事件;

    通过from的起始设置的时间,来每次触发时候相减来计算剩余时间,而不是像前面那种JS组件那边,每隔一秒加(减)1。

    3. 具体计时的是在何时触发的呢?

    $.extend(Counter.prototype, { 
        
    /* 共享所有的timer */ 
        _timer: setInterval(
    function() { $.counter._updateTargets(); }, 900), 
        
    /* 当前激活的timer列表 */ 
        _timerTargets: [],

        …

    });

    这里每隔900毫秒会执行一次事件(可以根据您的情况来设置)

    代码
    var remainTime = this._getTime(target);可以获得剩余的时间;

    /* 获取当前计数器的时间秒数 */ 
    _getTime: 
    function(target) { 
        
    var inst = $.data(target, DATA_NAME); 
        
    if (inst) { 
            
    var pattern = this._get(inst, 'pattern'); 
            
    if (pattern == 'down') { 
                
    var totalTime = this._get(inst, 'totalTime'); 
                
    var from = this._get(inst, 'from'); 
                
    var nowDate = new Date(); 
                
    var remainTime = parseInt((totalTime * 1000 - (nowDate.getTime() - from.getTime())) / 1000); 
                
    return remainTime; 
            } 
            
    else if (pattern == 'up') { 
                
    var from = this._get(inst, 'from'); 
                
    var nowDate = new Date(); 
                
    var remainTime = parseInt((nowDate.getTime() - from.getTime()) / 1000); 
                
    return remainTime; 
            } 
        } 
        
    return null
    }

    4. 通过getTime方法来获取剩余时间:

    代码
    /* 获取计时器当前时间(总秒数) */ 
    $.fn.getTime 
    = function() { 
        
    var args = Array.prototype.slice.call(arguments, 1); 
        
    if (this.length == 1) { 
            
    return $.counter['_getTime'].apply($.counter, [this[0]].concat(args)); 
        } 
        
    else if (this.length > 1) { 
            
    var array = []; 
            
    this.each(function() { 
            
    var time = $.counter['_getTime'].apply($.counter, [this].concat(args)); 
                
    if (time) { 
                    array.push(time); 
                } 
            }); 
            
    return array; 
        } 
        
    return null
    };

    调用方式为:

    $('#counter-1').getTime()

    5. 通过string参数来实现暂停和继续的功能:

    $('#btnPause').toggle(function() { 
        $(
    this).val('继续'); 
        $(
    '.counter').counter('pause'); 
    }, 
    function() { 
        $(
    this).val('暂停'); 
        $(
    '.counter').counter('resume'); 

    );

    其他的请看完整代码:

    代码
    /*
    * 计数器jQuery插件
    * Copyright: Leepy
    * Update: 2010-09-22
    * Home:   http://www.cnblogs.com/liping13599168/
    */

    (
    function($) {

        
    function Counter() {
            
    /* 计数器默认配置 */
            
    this._defaults = {
                pattern: 
    'down',    // 可选择参数:'up', 'down';默认方式为减计数
                totalTime: 3600,    // 总共需要多少时间,单位为秒
                until: null,        // 默认直到日期的配置
                onInterval: null,   // 间隔时间回调函数
                onStop: null        // 结束时回调函数
            }
        }

        
    var DATA_NAME = 'data_counter';

        $.extend(Counter.prototype, {
            
    /* 共享所有的timer */
            _timer: setInterval(
    function() { $.counter._updateTargets(); }, 900),
            
    /* 当前激活的timer列表 */
            _timerTargets: [],
            
    /* 更新每一个绑定的目标计数器 */
            _updateTargets: 
    function() {
                
    for (var i = 0, length = this._timerTargets.length; i < length; i++) {
                    
    this._updateCounter(this._timerTargets[i]);
                }
            },
            
    /* 绑定计数器 */
            _attachCounter: 
    function(target, options) {
                
    var inst = { options: $.extend({ 'from'new Date() }, options) };
                $.data(target, DATA_NAME, inst);
                
    this._changeCounter(target);
            },
            
    /* 重置计数器 */
            _changeCounter: 
    function(target) {
                
    this._addTarget(target);
                
    this._updateCounter(target);

            },
            
    /* 重新显示计数器 */
            _updateCounter: 
    function(target) {

                
    var remainTime = this._getTime(target);
                
    if (remainTime) {
                    
    //回调函数调用
                    var inst = $.data(target, DATA_NAME);
                    
    if (remainTime >= 0) {
                        
    var time = this._getFormatTime(remainTime);
                        $(target).html(time);

                        
    var onInterval = this._get(inst, 'onInterval');
                        
    if (onInterval) {
                            onInterval.apply(target, [remainTime]);
                        }
                    }
                    
    else {
                        remainTime 
    = 0;
                        
    var time = this._getFormatTime(remainTime);
                        $(target).html(time);

                        
    var onStop = this._get(inst, 'onStop');
                        
    if (onStop) {
                            onStop.apply(target, []);
                            
    this._removeTarget(target);
                        }
                    }
                }

            },
            
    /* 暂停计数器 */
            _pauseCounter: 
    function(target) {
                
    var inst = $.data(target, DATA_NAME);
                
    if (inst) {
                    
    var pauseTime = new Date();
                    $.extend(inst.options, { 
    'pauseTime': pauseTime });
                    $.data(target, DATA_NAME, inst);
                    
    this._removeTarget(target);
                }
            },
            
    /* 重新启动计数器 */
            _resumeCounter: 
    function(target) {
                
    var inst = $.data(target, DATA_NAME);
                
    if (inst) {
                    
    var nowDate = new Date();
                    
    var pauseTime = this._get(inst, 'pauseTime');
                    
    var from = this._get(inst, 'from');
                    
    if (pauseTime) {
                        
    var fromTime = new Date(from.getTime() + (nowDate.getTime() - pauseTime.getTime()));
                        $.extend(inst.options, { 
    'from': fromTime });
                        $.data(target, DATA_NAME, inst);
                        
    this._changeCounter(target);
                    }
                }
            },
            
    /* 获取当前计数器的时间秒数 */
            _getTime: 
    function(target) {
                
    var inst = $.data(target, DATA_NAME);
                
    if (inst) {
                    
    var pattern = this._get(inst, 'pattern');
                    
    if (pattern == 'down') {
                        
    var totalTime = this._get(inst, 'totalTime');
                        
    var from = this._get(inst, 'from');
                        
    var nowDate = new Date();
                        
    var remainTime = parseInt((totalTime * 1000 - (nowDate.getTime() - from.getTime())) / 1000);
                        
    return remainTime;
                    }
                    
    else if (pattern == 'up') {
                        
    var from = this._get(inst, 'from');
                        
    var nowDate = new Date();
                        
    var remainTime = parseInt((nowDate.getTime() - from.getTime()) / 1000);
                        
    return remainTime;
                    }
                }
                
    return null;
            },
            
    /* 获取格式化的时间 */
            _getFormatTime: 
    function(remainTime) {
                
    var hour = parseInt(remainTime / 3600);
                
    var min = parseInt(remainTime / 60% 60;
                
    var second = remainTime % 60;
                
    var time = this._stringFormat('{0}:{1}:{2}',
                        (hour 
    < 10 ? '0' + hour : hour),
                        (min 
    < 10 ? '0' + min : min),
                        (second 
    < 10 ? '0' + second : second));
                
    return time;
            },
            
    /* 从配置中获取指定名称的值 */
            _get: 
    function(inst, name) {
                
    return inst.options[name] != null ? inst.options[name] : $.counter._defaults[name];
            },
            
    /* 添加到目标计数器列表中 */
            _addTarget: 
    function(target) {
                
    if (!this._hasTarget(target)) this._timerTargets.push(target);
            },
            
    /* 是否已经包含在目标计数器列表中 */
            _hasTarget: 
    function(target) {
                
    return ($.inArray(target, this._timerTargets) > -1);
            },
            
    /* 移除指定目标计数器 */
            _removeTarget: 
    function(target) {
                
    this._timerTargets = $.map(this._timerTargets, function(o) { return (o == target ? null : o); });
            },
            
    //string格式化构造器
            _stringFormat: function(str) {
                
    var args = arguments;
                
    return str.replace(/\{(\d+)\}/g,
                    
    function(m, i) {
                        
    return args[parseInt(i) + 1];
                    });
            }
        });

        
    /* 主函数 */
        $.fn.counter 
    = function(options) {
            
    var args = Array.prototype.slice.call(arguments, 1);

            
    return this.each(function() {
                
    if (typeof options == 'string') {
                    $.counter[
    '_' + options + 'Counter'].apply($.counter, [this].concat(args));
                }
                
    else {
                    $.counter._attachCounter(
    this, options);
                }
            });
        };
        
    /* 获取计时器当前时间(总秒数) */
        $.fn.getTime 
    = function() {
            
    var args = Array.prototype.slice.call(arguments, 1);
            
    if (this.length == 1) {
                
    return $.counter['_getTime'].apply($.counter, [this[0]].concat(args));
            }
            
    else if (this.length > 1) {
                
    var array = [];
                
    this.each(function() {
                
    var time = $.counter['_getTime'].apply($.counter, [this].concat(args));
                    
    if (time) {
                        array.push(time);
                    }
                });
                
    return array;
            }
            
    return null;
        };

        $.counter 
    = new Counter();

    })(jQuery);
     

    附上源代码下载:CounterJSLab.rar

    最后啦,今天是中秋节,引用一下一位同事的祝福词咯!

    忍养安,乐养寿,爱养福,善养运,佛养心,道养行,学养德,诚养誉,礼养谊,动养身,天养地,古养今,问候养情谊! 祝各位博客园的园友中秋快乐!

    未命名

  • 相关阅读:
    实现Promise的first等各种变体
    js打乱数组的实战应用
    Vue单页面中进行业务数据的上报
    如何实现一个楼中楼的评论系统
    vue实现对表格数据的增删改查
    用CSS3实现无限循环的无缝滚动
    使用vue实现tab操作
    redis事务与关系型数据库事务比较
    优先队列原理与实现
    MySQL排序原理与案例分析
  • 原文地址:https://www.cnblogs.com/liping13599168/p/1833120.html
Copyright © 2011-2022 走看看