debounce.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. class Debounce {
  2. constructor() {
  3. this.timers = new Map();
  4. this.counters = new Map();
  5. this.v3 = new Map();
  6. }
  7. // 增加倒计时功能和连续点击触发功能
  8. // key: 唯一键
  9. // callback: 目标事件
  10. // interrupt: 中断事件
  11. // wait: 倒计时时间
  12. // 执行防抖操作的方法
  13. do(key, callback, wait = 300) {
  14. if (this.timers.has(key)) {
  15. clearTimeout(this.timers.get(key));
  16. }
  17. const timer = setTimeout(() => {
  18. callback();
  19. this.timers.delete(key);
  20. }, wait);
  21. this.timers.set(key, timer);
  22. }
  23. /**
  24. *
  25. * @param key
  26. * @param options
  27. * @param {number} [options.duration=10000] - 总倒计时时长(毫秒)。
  28. * @param {function} [options.callback=null] - 每秒回调事件。
  29. * @param {function} [options.endCallback=null] - 倒计时结束事件。
  30. * @param {function} [options.restart=false] - 再次点击是否重新计时。
  31. */
  32. countDown(key, options) {
  33. let op = {
  34. duration: options.duration || 10000, // 总倒计时时长(毫秒)
  35. callback: options.callback || null, // 每秒回调事件
  36. endCallback: options.endCallback || null, // 倒计时结束事件
  37. restart: options.restart || false, // 是否点击重新计时
  38. };
  39. if (op.restart || !this.counters.has(key)) {
  40. const tick = () => {
  41. const timer = this.counters.get(key);
  42. if (timer.remainingTime <= 0) {
  43. clearTimeout(timer.timeoutId);
  44. if (op.endCallback) op.endCallback();
  45. this.counters.delete(key);
  46. } else {
  47. timer.remainingTime -= 1000;
  48. if (op.callback) op.callback(Math.ceil(timer.remainingTime / 1000));
  49. timer.timeoutId = setTimeout(tick, 1000);
  50. this.counters.set(key, timer);
  51. }
  52. };
  53. this.counters.set(key, {
  54. remainingTime: op.duration,
  55. timeoutId: setTimeout(tick, 1000),
  56. });
  57. // 立即执行一次回调
  58. if (op.callback) op.callback(Math.ceil(op.duration / 1000));
  59. }
  60. }
  61. /**
  62. * 开始一个倒计时,支持每秒回调、成功和失败事件。
  63. * @param {string} key - 倒计时的唯一标识。
  64. * @param {{fail: fail, success: success, limit: number, run: run, time: number, repetition_time: number}} options - 倒计时的配置选项。
  65. * @param {number} [options.time=10000] - 总倒计时时长(毫秒)。
  66. * @param {number} [options.limit=5] - 连续操作的次数目标。
  67. * @param {function(second, count, boolean):void} [options.run] - 每秒执行的回调函数(剩余秒数,当前被点击次数,是否被点击)。
  68. * @param {function():void} [options.success] - 达到目标次数后的成功回调。
  69. * @param {function():void} [options.fail] - 倒计时结束的失败回调。
  70. * @param {boolean} [options.restart_time=false] - 是否点击重新计时。
  71. * @param {boolean} [options.repetition_time=0] - 重复点击限制时长(毫秒)。
  72. */
  73. secondCountDown(key, options = {}) {
  74. let op = {
  75. time: options.time || 10000, // 总倒计时长(毫秒)
  76. limit: options.limit || 5, // 连续操作的次数目标
  77. run: options.run || null, // 每秒都返回更改页面倒计时文字
  78. success: options.success || null, // 达到目标次数后的成功回调
  79. fail: options.fail || null, // 倒计时结束的失败回调
  80. restart_time: options.restart_time || false, // 是否点击重新计时
  81. repetition_time: options.repetition_time || -1, // 重复点击限制时长(毫秒) -1 为不重置
  82. };
  83. let counter = this.counters.get(key) || {
  84. type: 2,
  85. count: 0,
  86. timer: null,
  87. startTime: Date.now(),
  88. remainingTime: op.time,
  89. inform: true,
  90. repetition: null,
  91. };
  92. if (counter.timer) {
  93. clearTimeout(counter.timer);
  94. counter.timer = null;
  95. }
  96. // 仅当op.restart_time为true时重置时间,点击次数不重置
  97. if (op.restart_time) {
  98. counter.startTime = Date.now();
  99. counter.remainingTime = op.time;
  100. counter.inform = true;
  101. }
  102. const tick = () => {
  103. let elapsed = Date.now() - counter.startTime;
  104. let remaining = op.time - elapsed;
  105. if (remaining <= 0) {
  106. // 时间到,执行失败事件
  107. op.fail && op.fail();
  108. this.counters.delete(key);
  109. } else {
  110. // 更新剩余时间并执行运行回调
  111. counter.remainingTime = remaining;
  112. op.run && op.run(Math.ceil(remaining / 1000), counter.count, counter.inform);
  113. counter.inform = false;
  114. counter.timer = setTimeout(tick, 1000);
  115. this.counters.set(key, counter);
  116. }
  117. };
  118. counter.count += 1;
  119. counter.inform = true // 每次点击都重新通知
  120. if (counter.count >= op.limit) {
  121. // 达到点击次数,执行成功事件
  122. clearTimeout(counter.timer);
  123. op.success && op.success();
  124. if (op.repetition_time < 0) {
  125. } else if (op.repetition_time === 0) {
  126. this.counters.delete(key);
  127. } else if (op.repetition_time > 0 && !counter.repetition) {
  128. let that = this
  129. counter.repetition = setTimeout(() => {
  130. that.counters.delete(key);
  131. }, op.repetition_time)
  132. this.counters.set(key, counter);
  133. }
  134. } else {
  135. // 更新计数器状态
  136. this.counters.set(key, counter);
  137. // 继续倒计时
  138. if (!counter.timer) {
  139. tick();
  140. }
  141. }
  142. }
  143. clearDebounce(key) {
  144. let counter = this.counters.get(key)
  145. if (!counter) return;
  146. if (counter.timer) clearTimeout(counter.timer)
  147. switch (counter.type) {
  148. case 2:
  149. if (counter.repetition) clearTimeout(counter.repetition)
  150. break;
  151. }
  152. this.counters.delete(key)
  153. }
  154. }
  155. export default Debounce