tui-countdown.vue 7.3 KB

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