You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

171 lines
4.6 KiB

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