u-count-down.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. <template>
  2. <view class="u-count-down">
  3. <slot>
  4. <text class="u-count-down__text" :style="customStyle">{{ formattedTime }}</text>
  5. </slot>
  6. </view>
  7. </template>
  8. <script>
  9. import { isSameSecond, parseFormat, parseTimeData } from "./utils";
  10. /**
  11. * u-count-down 倒计时
  12. * @description 该组件一般使用于某个活动的截止时间上,通过数字的变化,给用户明确的时间感受,提示用户进行某一个行为操作。
  13. * @tutorial https://uviewui.com/components/countDown.html
  14. * @property {String | Number} timestamp 倒计时时长,单位ms (默认 0 )
  15. * @property {String} format 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒 (默认 'HH:mm:ss' )
  16. * @property {Boolean} autoStart 是否自动开始倒计时 (默认 true )
  17. * @event {Function} end 倒计时结束时触发
  18. * @event {Function} change 倒计时变化时触发
  19. * @event {Function} start 开始倒计时
  20. * @event {Function} pause 暂停倒计时
  21. * @event {Function} reset 重设倒计时,若 auto-start 为 true,重设后会自动开始倒计时
  22. * @example <u-count-down :timestamp="timestamp"></u-count-down>
  23. */
  24. export default {
  25. name: "u-count-down",
  26. emits: ["change", "end", "finish"],
  27. props: {
  28. // 倒计时时长,单位ms
  29. timestamp: {
  30. type: [String, Number],
  31. default: 0
  32. },
  33. // 时间格式,DD-日,HH-时,mm-分,ss-秒,SSS-毫秒
  34. format: {
  35. type: String,
  36. default: "DD:HH:mm:ss"
  37. },
  38. // 是否自动开始倒计时
  39. autoStart: {
  40. type: Boolean,
  41. default: true
  42. },
  43. customStyle: {
  44. type: [String, Object],
  45. default: ""
  46. }
  47. },
  48. data() {
  49. return {
  50. timer: null,
  51. // 各单位(天,时,分等)剩余时间
  52. timeData: parseTimeData(0),
  53. // 格式化后的时间,如"03:23:21"
  54. formattedTime: "0",
  55. // 倒计时是否正在进行中
  56. runing: false,
  57. endTime: 0, // 结束的毫秒时间戳
  58. remainTime: 0 // 剩余的毫秒时间
  59. };
  60. },
  61. watch: {
  62. timestamp(n) {
  63. this.reset();
  64. },
  65. format(newVal, oldVal) {
  66. this.pause();
  67. this.start();
  68. }
  69. },
  70. mounted() {
  71. this.init();
  72. },
  73. methods: {
  74. init() {
  75. this.reset();
  76. },
  77. // 开始倒计时
  78. start() {
  79. if (this.runing) return;
  80. // 标识为进行中
  81. this.runing = true;
  82. // 结束时间戳 = 此刻时间戳 + 剩余的时间
  83. this.endTime = Date.now() + this.remainTime;
  84. this.toTick();
  85. },
  86. // 根据是否展示毫秒,执行不同操作函数
  87. toTick() {
  88. if (this.format.indexOf("SSS") > -1) {
  89. this.microTick();
  90. } else {
  91. this.macroTick();
  92. }
  93. },
  94. macroTick() {
  95. this.clearTimeout();
  96. // 每隔一定时间,更新一遍定时器的值
  97. // 同时此定时器的作用也能带来毫秒级的更新
  98. this.timer = setTimeout(() => {
  99. // 获取剩余时间
  100. const remain = this.getRemainTime();
  101. // 重设剩余时间
  102. if (!isSameSecond(remain, this.remainTime) || remain === 0) {
  103. this.setRemainTime(remain);
  104. }
  105. // 如果剩余时间不为0,则继续检查更新倒计时
  106. if (this.remainTime !== 0) {
  107. this.macroTick();
  108. }
  109. }, 30);
  110. },
  111. microTick() {
  112. this.clearTimeout();
  113. this.timer = setTimeout(() => {
  114. this.setRemainTime(this.getRemainTime());
  115. if (this.remainTime !== 0) {
  116. this.microTick();
  117. }
  118. }, 30);
  119. },
  120. // 获取剩余的时间
  121. getRemainTime() {
  122. // 取最大值,防止出现小于0的剩余时间值
  123. return Math.max(this.endTime - Date.now(), 0);
  124. },
  125. // 设置剩余的时间
  126. setRemainTime(remain) {
  127. this.remainTime = remain;
  128. // 根据剩余的毫秒时间,得出该有天,小时,分钟等的值,返回一个对象
  129. const timeData = parseTimeData(remain);
  130. this.$emit("change", timeData);
  131. // 得出格式化后的时间
  132. this.formattedTime = parseFormat(this.format, timeData);
  133. // 如果时间已到,停止倒计时
  134. if (remain <= 0) {
  135. this.pause();
  136. this.$emit("end");
  137. this.$emit("finish");
  138. }
  139. },
  140. // 重置倒计时
  141. reset() {
  142. this.pause();
  143. this.remainTime = this.timestamp;
  144. this.setRemainTime(this.remainTime);
  145. if (this.autoStart) {
  146. this.start();
  147. }
  148. },
  149. // 暂停倒计时
  150. pause() {
  151. this.runing = false;
  152. this.clearTimeout();
  153. },
  154. // 清空定时器
  155. clearTimeout() {
  156. clearTimeout(this.timer);
  157. this.timer = null;
  158. }
  159. },
  160. // #ifdef VUE2
  161. beforeDestroy() {
  162. this.clearTimeout();
  163. },
  164. // #endif
  165. // #ifdef VUE3
  166. beforeUnmount() {
  167. this.clearTimeout();
  168. },
  169. // #endif
  170. };
  171. </script>
  172. <style lang="scss"></style>