tui-timer.vue 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. <template>
  2. <view class="tui-timer__box">
  3. <view class="tui-timer__item" :style="{
  4. background: backgroundColor,
  5. border: borderWidth > 0 ? `${borderWidth}rpx solid ${borderColor}` : 0,
  6. width: backgroundColor == 'transparent' && borderColor == 'transparent' ? 'auto' : getWidth(d, width) + 'rpx',
  7. height: height + 'rpx',
  8. fontSize: size + 'rpx',
  9. color: color
  10. }"
  11. v-if="isDays">
  12. {{ d }}
  13. </view>
  14. <view class="tui-timer__colon" :style="{ lineHeight: colonSize + 'rpx', fontSize: colonSize + 'rpx', color: colonColor }"
  15. v-if="isDays">天</view>
  16. <view class="tui-timer__item" :style="{
  17. background: backgroundColor,
  18. border: borderWidth > 0 ? `${borderWidth}rpx solid ${borderColor}` : 0,
  19. width: getWidth(d, width) + 'rpx',
  20. height: height + 'rpx',
  21. fontSize: size + 'rpx',
  22. color: color
  23. }"
  24. v-if="isHours">
  25. {{ h }}
  26. </view>
  27. <view class="tui-timer__colon" :style="{ lineHeight: colonSize + 'rpx', fontSize: colonSize + 'rpx', color: colonColor }"
  28. v-if="isHours">{{ isColon ? ':' : '时' }}</view>
  29. <view class="tui-timer__item" :style="{
  30. background: backgroundColor,
  31. border: borderWidth > 0 ? `${borderWidth}rpx solid ${borderColor}` : 0,
  32. width: getWidth(d, width) + 'rpx',
  33. height: height + 'rpx',
  34. fontSize: size + 'rpx',
  35. color: color
  36. }"
  37. v-if="isMinutes">
  38. {{ m }}
  39. </view>
  40. <view class="tui-timer__colon" :style="{ lineHeight: colonSize + 'rpx', fontSize: colonSize + 'rpx', color: colonColor }"
  41. v-if="isMinutes">{{ isColon ? ':' : '分' }}</view>
  42. <view class="tui-timer__item" :style="{
  43. background: backgroundColor,
  44. border: borderWidth > 0 ? `${borderWidth}rpx solid ${borderColor}` : 0,
  45. width: getWidth(d, width) + 'rpx',
  46. height: height + 'rpx',
  47. fontSize: size + 'rpx',
  48. color: color
  49. }"
  50. v-if="isSeconds">
  51. {{ s }}
  52. </view>
  53. <view class="tui-timer__colon" :style="{ lineHeight: colonSize + 'rpx', fontSize: colonSize + 'rpx', color: colonColor }"
  54. v-if="isSeconds">{{ isColon ? '' : '秒' }}</view>
  55. <view class="tui-timer__colon" :style="{ lineHeight: colonSize + 'rpx', fontSize: colonSize + 'rpx', color: colonColor }"
  56. v-if="isSeconds && isMs">.</view>
  57. <view class="tui-timer__ms" :style="{
  58. background: backgroundColor,
  59. border: borderWidth > 0 ? `${borderWidth}rpx solid ${borderColor}` : 0,
  60. fontSize: msSize + 'rpx',
  61. color: msColor,
  62. height: height + 'rpx',
  63. width: msWidth > 0 ? msWidth + 'rpx' : 'auto'
  64. }"
  65. v-if="isSeconds && isMs">
  66. <view :class="{ 'tui-ms__list': ani }">
  67. <view class="tui-ms__item" :style="{ height: height + 'rpx' }" v-for="(item, index) in ms" :key="index">{{ item }}</view>
  68. </view>
  69. </view>
  70. </view>
  71. </template>
  72. <script>
  73. export default {
  74. name: 'tuiTimer',
  75. emits: ['end'],
  76. props: {
  77. //数字框宽度
  78. width: {
  79. type: Number,
  80. default: 36
  81. },
  82. //数字框高度
  83. height: {
  84. type: Number,
  85. default: 36
  86. },
  87. borderWidth: {
  88. type: Number,
  89. default: 0
  90. },
  91. //数字框border颜色
  92. borderColor: {
  93. type: String,
  94. default: 'transparent'
  95. },
  96. //数字框背景颜色
  97. backgroundColor: {
  98. type: String,
  99. default: 'transparent'
  100. },
  101. //数字框字体大小
  102. size: {
  103. type: Number,
  104. default: 32
  105. },
  106. //数字框字体颜色
  107. color: {
  108. type: String,
  109. default: '#333'
  110. },
  111. //冒号或文字大小
  112. colonSize: {
  113. type: Number,
  114. default: 32
  115. },
  116. //冒号或文字颜色
  117. colonColor: {
  118. type: String,
  119. default: '#333'
  120. },
  121. //初始时间,单位s
  122. value: {
  123. type: [Number,String],
  124. default: 0
  125. },
  126. //最大时间,大于等于最大时间则计时结束,为0则需要手动结束 (单位:秒)
  127. maxTime: {
  128. type: Number,
  129. default: 0
  130. },
  131. //是否显示天
  132. isDays: {
  133. type: Boolean,
  134. default: false
  135. },
  136. //是否显示小时
  137. isHours: {
  138. type: Boolean,
  139. default: true
  140. },
  141. //是否显示分钟
  142. isMinutes: {
  143. type: Boolean,
  144. default: true
  145. },
  146. //是否显示秒数
  147. isSeconds: {
  148. type: Boolean,
  149. default: true
  150. },
  151. //是否显示毫秒
  152. isMs: {
  153. type: Boolean,
  154. default: false
  155. },
  156. msWidth: {
  157. type: Number,
  158. default: 0
  159. },
  160. msSize: {
  161. type: Number,
  162. default: 28
  163. },
  164. msColor: {
  165. type: String,
  166. default: '#333'
  167. },
  168. //时分秒是否展示为冒号,false为文字
  169. isColon: {
  170. type: Boolean,
  171. default: true
  172. },
  173. //是否自动开始(传值false,则需要手动调用方法)
  174. start: {
  175. type: Boolean,
  176. default: true
  177. }
  178. },
  179. data() {
  180. return {
  181. d: '0',
  182. h: '00',
  183. m: '00',
  184. s: '00',
  185. ms: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
  186. seconds: 0,
  187. loop: null,
  188. ani: false
  189. };
  190. },
  191. created() {
  192. this.seconds = Number(this.value);
  193. this.timer(this.seconds);
  194. if (this.start) {
  195. this.startTiming();
  196. }
  197. },
  198. // #ifndef VUE3
  199. beforeDestroy() {
  200. this.clearTimer();
  201. },
  202. // #endif
  203. // #ifdef VUE3
  204. beforeUnmount() {
  205. this.clearTimer();
  206. },
  207. // #endif
  208. watch: {
  209. value(val) {
  210. this.clearTimer();
  211. this.seconds = Number(val);
  212. this.timer(this.seconds);
  213. setTimeout(() => {
  214. if (this.start) {
  215. this.startTiming();
  216. }
  217. }, 0);
  218. }
  219. },
  220. methods: {
  221. getWidth(num, width) {
  222. return num > 99 ? (width / 2) * num.toString().length : width;
  223. },
  224. clearTimer() {
  225. this.ani = false;
  226. clearInterval(this.loop);
  227. this.loop = null;
  228. },
  229. //开始
  230. startTiming() {
  231. if (this.seconds >= this.maxTime && this.maxTime != 0) {
  232. this.endTimer();
  233. return
  234. }
  235. this.clearTimer();
  236. this.ani = true;
  237. this.loop = setInterval(() => {
  238. this.seconds++;
  239. this.timer(this.seconds);
  240. if (this.seconds >= this.maxTime && this.maxTime != 0) {
  241. this.endTimer();
  242. }
  243. }, 1000);
  244. },
  245. //重置
  246. resetTimer() {
  247. this.d = '0';
  248. this.h = '00';
  249. this.m = '00';
  250. this.s = '00';
  251. this.seconds = 0;
  252. this.clearTimer();
  253. setTimeout(() => {
  254. this.startTiming();
  255. }, 0);
  256. },
  257. //结束 | 暂停
  258. endTimer() {
  259. this.clearTimer();
  260. this.$emit('end', {
  261. day: this.d,
  262. hour: this.h,
  263. minute: this.m,
  264. second: this.s,
  265. totalSeconds: this.seconds
  266. });
  267. },
  268. timer(seconds) {
  269. let [day, hour, minute, second] = [0, 0, 0, 0];
  270. if (seconds > 0) {
  271. day = this.isDays ? Math.floor(seconds / (60 * 60 * 24)) : 0;
  272. hour = this.isHours ? Math.floor(seconds / (60 * 60)) - day * 24 : 0;
  273. minute = this.isMinutes ? Math.floor(seconds / 60) - hour * 60 - day * 24 * 60 : 0;
  274. second = this.isSeconds ? Math.floor(seconds) - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60 : 0;
  275. }
  276. hour = hour < 10 ? '0' + hour : hour;
  277. minute = minute < 10 ? '0' + minute : minute;
  278. second = second < 10 ? '0' + second : second;
  279. this.d = day;
  280. this.h = hour;
  281. this.m = minute;
  282. this.s = second;
  283. }
  284. }
  285. };
  286. </script>
  287. <style scoped>
  288. .tui-timer__box {
  289. display: inline-flex;
  290. align-items: center;
  291. justify-content: center;
  292. }
  293. .tui-timer__item,
  294. .tui-timer__colon {
  295. display: flex;
  296. align-items: center;
  297. justify-content: center;
  298. border-radius: 6rpx;
  299. }
  300. .tui-timer__ms {
  301. overflow: hidden;
  302. border-radius: 6rpx;
  303. }
  304. /*ms使用css3代替js频繁更新操作,性能优化*/
  305. .tui-ms__list {
  306. animation: loop 1s steps(10) infinite;
  307. }
  308. @keyframes loop {
  309. from {
  310. transform: translateY(0);
  311. }
  312. to {
  313. transform: translateY(-100%);
  314. }
  315. }
  316. .tui-ms__item {
  317. display: flex;
  318. align-items: center;
  319. justify-content: center;
  320. }
  321. </style>