Blame view

public/js/product/outlet/countdown.js 9.33 KB
1 2 3 4 5 6
/**
 * countdown.js.
 * @author hgwang
 * @date 2016-05-29
 */
'use strict';
lijing authored
7
let $ = require('yoho-jquery');
陈峰 authored
8
let tip = require('js/plugin/tip');
lijing authored
9 10
let EVENT_AFTER_PAINT = 'afterPaint';
let defaultOPtions = {
11
    el: {},
王洪广 authored
12
13 14 15
    // unix时间戳,单位应该是毫秒!
    stopPoint: 0,
    leftTime: 0,
王洪广 authored
16
    template: '', // '${h}时${m}分${s-ext}秒'
17 18 19
    varRegular: /\$\{([\-\w]+)\}/g,
    clock: ['d', 100, 2, 'h', 24, 2, 'm', 60, 2, 's', 60, 2, 'u', 10, 1],
    effect: 'normal'
王洪广 authored
20
};
lijing authored
21
let effect = {
22
    normal: {
王洪广 authored
23
        paint: function() {
lijing authored
24
            let me = this,
25 26 27
                content;

            // 找到值发生改变的hand
王洪广 authored
28
            $.each(me.hands, function(index, hand) {
29 30 31 32
                if (hand.lastValue !== hand.value) {
                    // 生成新的markup
                    content = '';
王洪广 authored
33
                    $.each(me._toDigitals(hand.value, hand.bits), function(i, digital) {
34 35 36 37 38 39 40 41 42
                        content += me._html(digital, '', 'digital');
                    });

                    // 并更新
                    hand.node.html(content);
                }
            });
        }
    }
王洪广 authored
43
};
lijing authored
44 45
let timer = (function() {
    let fns = [],
46 47 48 49 50 51
        commands = [];// 操作指令

    /**
     * timer
     * 调用频率为100ms一次。努力精确计时,调用帧函数
     */
王洪广 authored
52 53
    function timerIn() {
        // 计算新时间,调整diff
lijing authored
54
        let diff = +new Date() - timerIn.nextTime,
王洪广 authored
55 56 57
            count = 1 + Math.floor(diff / 100);

        // 循环处理fns二元组
lijing authored
58
        let frequency, step,
王洪广 authored
59 60
            i, len;
61 62 63 64 65 66 67
        // 为避免循环时受到 对fns数组操作 的影响,
        // add/remove指令提前统一处理
        while (commands.length) {
            commands.shift()();
        }

        diff = 100 - diff % 100;
王洪广 authored
68 69
        timerIn.nextTime += 100 * count;
70 71 72 73 74

        for (i = 0, len = fns.length; i < len; i += 2) {
            frequency = fns[i + 1];

            // 100次/s的
王洪广 authored
75
            if (frequency === 0) {
76
                fns[i](count);
王洪广 authored
77
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
                // 1000次/s的
            } else {
                // 先把末位至0,再每次加2
                frequency += 2 * count - 1;

                step = Math.floor(frequency / 20);
                if (step > 0) {
                    fns[i](step);
                }

                // 把末位还原成1
                fns[i + 1] = frequency % 20 + 1;
            }
        }

        // next
王洪广 authored
94
        setTimeout(timerIn, diff);
95 96 97
    }

    // 首次调用
王洪广 authored
98 99
    timerIn.nextTime = +new Date();
    timerIn();
100
    function indexOf(item, arr) {
lijing authored
101
        let i, len;
王洪广 authored
102 103

        for (i = 0, len = arr.length; i < len; ++i) {
104 105 106 107 108 109 110 111
            if (arr[i] === item) {
                return i;
            }
        }
        return -1;
    }

    return {
王洪广 authored
112 113
        add: function(fn, frequency) {
            commands.push(function() {
114 115 116 117
                fns.push(fn);
                fns.push(frequency === 1000 ? 1 : 0);
            });
        },
王洪广 authored
118
        remove: function(fn) {
lijing authored
119
            let i;
王洪广 authored
120 121 122

            commands.push(function() {
                i = indexOf(fn, fns);
123 124 125 126 127 128
                if (i !== -1) {
                    fns.splice(indexOf(fn, fns), 2);
                }
            });
        }
    };
王洪广 authored
129 130
}());
131
function Countdown(config) {
lijing authored
132
    let cfg;
王洪广 authored
133
134 135 136 137 138
    if (!(this instanceof Countdown)) {
        return new Countdown(config);
    }

    config.el = $(config.el);
王洪广 authored
139 140 141 142 143
    if (!config.el) {
        return;
    }

    cfg = config.el.attr('data-config');
144 145 146 147 148 149 150 151 152 153 154 155 156 157

    if (cfg) {
        cfg = JSON.parse(cfg.replace(/'/g, '"'));
        config = $.extend(true, {}, defaultOPtions, cfg, config);
    }

    this.config = config;
    this._init();
}
$.extend(Countdown.prototype, {
    /**
     * 初始化
     * @private
     */
王洪广 authored
158
    _init: function() {
lijing authored
159 160
        let me = this;
        let el = me.config.el;
161 162

        // 初始化时钟.
lijing authored
163
        let hands = [];
王洪广 authored
164 165

        // 分析markup
lijing authored
166 167
        let tmpl = el.html();
        let varRE = me.config.varRegular;
王洪广 authored
168
lijing authored
169 170
        let clock;
        let _reflow;
王洪广 authored
171
172 173 174
        /**
         * 指针结构
         * hand: {
王洪广 authored
175 176 177 178 179 180 181 182
         *   type: string,
         *   value: number,
         *   lastValue: number,
         *   base: number,
         *   radix: number,
         *   bits: number,
         *   node: S.Node
         * }
183 184 185 186 187 188
         */
        me.hands = hands;
        me.frequency = 1000;
        me._notify = [];

        varRE.lastIndex = 0;
王洪广 authored
189 190
        el.html(tmpl.replace(varRE, function(str, type) {
            // 生成hand的markup
lijing authored
191
            let content = '';
王洪广 authored
192
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
            // 时钟频率校正.
            if (type === 'u' || type === 's-ext') {
                me.frequency = 100;
            }

            if (type === 's-ext') {
                hands.push({type: 's'});
                hands.push({type: 'u'});
                content = me._html('', 's', 'handlet') +
                    me._html('.', '', 'digital') +
                    me._html('', 'u', 'handlet');
            } else {
                hands.push({type: type});
            }

            return me._html(content, type, 'hand');
        }));

        // 指针type以外属性(node, radix, etc.)的初始化.
王洪广 authored
212 213 214
        clock = me.config.clock;

        $.each(hands, function(index, hand) {
lijing authored
215
            let type = hand.type,
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
                base = 100, i;

            hand.node = el.find('.hand-' + type);

            // radix, bits 初始化.
            for (i = clock.length - 3; i > -1; i -= 3) {
                if (type === clock[i]) {
                    break;
                }

                base *= clock[i + 1];
            }
            hand.base = base;
            hand.radix = clock[i + 1];
            hand.bits = clock[i + 2];
        });

        me._getLeft();
        me._reflow();

        // bind reflow to me.
王洪广 authored
237 238
        _reflow = me._reflow;
        me._reflow = function() {
239 240 241 242 243 244 245
            return _reflow.apply(me, arguments);
        };
        timer.add(me._reflow, me.frequency);

        // 显示时钟.
        el.show();
    },
王洪广 authored
246
247 248 249
    /**
     * 获取倒计时剩余帧数
     */
王洪广 authored
250
    _getLeft: function() { // {{{
lijing authored
251
        let left = this.config.leftTime * 1000;
毕凯 authored
252
        let end = this.config.stopPoint; // 这个是UNIX时间戳,毫秒级
王洪广 authored
253
254 255 256 257 258
        if (!left && end) {
            left = end - (+new Date());
        }

        this.left = left - left % this.frequency;
王洪广 authored
259
    }, // }}}
260 261 262
    /**
     * 更新时钟
     */
王洪广 authored
263
    _reflow: function(count) {
lijing authored
264 265
        let me = this;
        let el = me.config.el;
王洪广 authored
266 267

        count = count || 0;
268 269 270
        me.left = me.left - me.frequency * count;

        // 更新hands
王洪广 authored
271
        $.each(me.hands, function(index, hand) {
272 273 274 275 276 277 278 279 280
            hand.lastValue = hand.value;
            hand.value = Math.floor(me.left / hand.base) % hand.radix;
        });

        // 更新时钟.
        me._repaint();

        // notify
        if (me._notify[me.left]) {
王洪广 authored
281
            $.each(me._notify[me.left], function(index, callback) {
282 283 284 285 286 287
                callback.call(me);
            });
        }

        // notify 可能更新me.left
        if (me.left < 1) {
runner authored
288
            el.text('— 活动已结束 —');
289 290 291 292
            el.parents('.back-ground-white').on('click', function() {
                tip.show('活动即将开始,敬请期待!');
                return false;
            });
293 294 295
            timer.remove(me._reflow);
        }
liangxs authored
296 297 298
        if (me.left < 86400000) {
            el.find('.left-day').hide();
        }
299 300
        return me;
    },
王洪广 authored
301
302 303 304 305
    /**
     * 重绘时钟
     * @private
     */
王洪广 authored
306
    _repaint: function() {
307 308 309 310
        effect[this.config.effect].paint.apply(this);

        this.config.el.trigger(EVENT_AFTER_PAINT);
    },
王洪广 authored
311
312 313 314 315 316 317
    /**
     * 把值转换为独立的数字形式
     * @private
     * @param {number} value
     * @param {number} bits
     */
王洪广 authored
318
    _toDigitals: function(value, bits) {
lijing authored
319
        let digitals = [];
320
王洪广 authored
321
        value = value < 0 ? 0 : value;
王洪广 authored
322
        value.toString().length > 1 ? bits = 2 : bits = 1;
王洪广 authored
323
324 325 326 327 328 329 330 331 332
        // 把时、分、秒等换算成数字.
        while (bits--) {
            digitals[bits] = value % 10;

            value = Math.floor(value / 10);
        }

        return digitals;
    },
王洪广 authored
333
334 335 336 337 338 339 340
    /**
     * 生成需要的html代码,辅助工具
     * @private
     * @param {string|Array.<string>} content
     * @param {string} className
     * @param {string} type
     */
王洪广 authored
341
    _html: function(content, className, type) {
342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
        if ($.isArray(content)) {
            content = content.join('');
        }

        switch (type) {
            case 'hand':
                className = type + ' hand-' + className;
                break;
            case 'handlet':
                className = type + ' hand-' + className;
                break;
            case 'digital':
                if (content === '.') {
                    className = type + ' ' + type + '-point ' + className;
                } else {
                    className = type + ' ' + type + '-' + content + ' ' + className;
                }
                break;
biao authored
360 361
            default:
                break;
362 363 364 365
        }

        return '<i class="' + className + '">' + content + '</i>';
    },
王洪广 authored
366
367 368 369 370 371
    /**
     * 倒计时事件
     * @param {number} time unit: second
     * @param {Function} callback
     */
王洪广 authored
372
    notify: function(time, callback) {
lijing authored
373
        let notifies;
王洪广 authored
374
375 376 377
        time = time * 1000;
        time = time - time % this.frequency;
王洪广 authored
378
        notifies = this._notify[time] || [];
379 380 381 382 383 384
        notifies.push(callback);
        this._notify[time] = notifies;

        return this;
    }
});
runner authored
385
exports.Countdown = Countdown;