Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | 1x 1x 1x 1x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 5x 308x 308x 308x 21x 21x 21x 5x 1x 5x 1x 1x 1x 1x 5x 1x 5x 308x 308x 308x 3x 5x 5x 5x 5x 5x 4x 5x 1x 1x 1x 5x 5x 3x 3x 3x 310x 310x 310x 310x 308x 308x 2x 2x 2x 3x 5x 5x 3x 5x 5x 1x 1x | /** * 提供倒计时器统一封装 * @method time/countDown * @param {Object} spec 选项 * @param {Date} [spec.base] 矫正时间,如果需要用服务端时间矫正倒计时,使用此参数 * @param {Date} [spec.target=Date.now() + 3000] 目标时间 * @param {Number} [spec.interval=1000] 倒计时触发间隔 * @param {Function} [spec.onChange] 倒计时触发变更的事件回调 * @param {Function} [spec.onStop] 倒计时结束的回调 * @returns {Object} 倒计时对象实例 * @example * var $countDown = require('@spore-ui/kit/packages/time/countDown'); * var target = Date.now() + 5000; * var cd1 = $countDown({ * target : target, * onChange : function(delta){ * console.info('cd1 change', delta); * }, * onStop : function(delta){ * console.info('cd1 stop', delta); * } * }); * setTimeout(function(){ * //trigger stop * cd1.stop(); * }, 2000); * var cd2 = countDown({ * target : target, * interval : 2000, * onChange : function(delta){ * console.info('cd2 change', delta); * }, * onStop : function(delta){ * console.info('cd2 stop', delta); * } * }); */ var $erase = require('../arr/erase'); var $assign = require('../obj/assign'); var allMonitors = {}; var localBaseTime = Date.now(); function countDown(spec) { var that = {}; // 为什么不使用 timeLeft 参数替换 base 和 target: // 如果用 timeLeft 作为参数,计时器初始化之前如果有进程阻塞,有可能会导致与目标时间产生误差 // 页面多个定时器一起初始化时,会出现计时器更新不同步的现象,同时引发页面多处没有一起回流 // 保留目前这个方案,用于需要精确倒计时的情况 var conf = $assign({ base: null, target: Date.now() + 3000, interval: 1000, onChange: null, onStop: null, }, spec); var timeDiff = 0; var target = +new Date(conf.target); var interval = parseInt(conf.interval, 10) || 0; var onChange = conf.onChange; var onStop = conf.onStop; // 使倒计时触发时间精确化 // 使用固定的触发频率,减少需要创建的定时器 var timeInterval = interval; Eif (timeInterval < 500) { timeInterval = 10; } else if (timeInterval < 5000) { timeInterval = 100; } else { timeInterval = 1000; } var delta; var curUnit; var update = function (now) { delta = target - now; var unit = Math.ceil(delta / interval); if (unit !== curUnit) { curUnit = unit; Eif (typeof onChange === 'function') { onChange(delta); } } }; /** * 重设目标时间 * @method countDown#setTarget * @memberof time/countDown * @example * var cd = countDown(); * var localTime = '2019/01/01'; * cd.setTarget(serverTime); */ that.setTarget = function (time) { target = +new Date(time); }; /** * 纠正时间差 * @method countDown#correct * @memberof time/countDown * @example * var cd = countDown(); * var serverTime = '2019/01/01'; * var localTime = '2020/01/01'; * cd.correct(serverTime); * cd.correct(serverTime, localTime); */ that.correct = function (serverTime, localTime) { var now = localTime ? new Date(localTime) : +new Date(); var serverDate = serverTime ? new Date(serverTime) : 0; Eif (serverDate) { timeDiff = serverDate - now; } }; if (conf.base) { that.correct(conf.base); } var check = function (localDelta) { var now = localBaseTime + timeDiff + localDelta; update(now); if (delta <= 0) { that.stop(now); } }; /** * 停止倒计时 * @method countDown#stop * @memberof time/countDown * @example * var cd = countDown(); * cd.stop(); */ that.stop = function () { var mobj = allMonitors[timeInterval]; Eif (mobj && mobj.queue) { $erase(mobj.queue, check); } // onStop事件触发必须在从队列移除回调之后 // 否则循环接替的定时器会引发死循环 if (typeof onStop === 'function') { onStop(delta); } }; /** * 销毁倒计时 * @method countDown#destroy * @memberof time/countDown * @example * var cd = countDown(); * cd.destroy(); */ that.destroy = function () { onChange = null; onStop = null; that.stop(); }; var monitor = allMonitors[timeInterval]; if (!monitor) { monitor = {}; monitor.queue = []; monitor.inspect = function () { var now = Date.now(); var mobj = allMonitors[timeInterval]; var localDelta = now - localBaseTime; if (mobj.queue.length) { // 循环当中会删除数组元素,因此需要先复制一下数组 mobj.queue.slice(0).forEach(function (fn) { fn(localDelta); }); } else { clearInterval(mobj.timer); allMonitors[timeInterval] = null; delete mobj.timer; } }; allMonitors[timeInterval] = monitor; } monitor.queue.push(check); if (!monitor.timer) { monitor.timer = setInterval(monitor.inspect, timeInterval); } monitor.inspect(); return that; } countDown.allMonitors = allMonitors; module.exports = countDown; |