index.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. <template>
  2. <div class="go-flipper" :class="[flipType, { go: isFlipping }]">
  3. <div class="digital front" :data-front="frontTextFromData"></div>
  4. <div class="digital back" :data-back="backTextFromData"></div>
  5. </div>
  6. </template>
  7. <script lang="ts" setup>
  8. import { ref, PropType, watch, nextTick } from 'vue'
  9. import { FlipType } from './index'
  10. const props = defineProps({
  11. flipType: {
  12. type: String as PropType<FlipType>,
  13. default: () => {
  14. return 'down'
  15. }
  16. },
  17. count: {
  18. type: [Number, String],
  19. default: 0
  20. },
  21. duration: {
  22. type: Number,
  23. default: 600
  24. },
  25. width: {
  26. type: Number,
  27. default: 60
  28. },
  29. height: {
  30. type: Number,
  31. default: 100
  32. },
  33. radius: {
  34. type: Number,
  35. default: 10
  36. },
  37. frontColor: {
  38. type: String,
  39. default: '#ffffff'
  40. },
  41. backColor: {
  42. type: String,
  43. default: '#000000'
  44. },
  45. borderWidth: {
  46. type: Number,
  47. default: 2
  48. }
  49. })
  50. const isFlipping = ref(false)
  51. const frontTextFromData = ref(props.count || 0)
  52. const backTextFromData = ref(props.count || 0)
  53. let timeoutID: any = 0
  54. // 翻牌
  55. const flip = async (front: string | number, back: string | number) => {
  56. // 如果处于翻转中,则不执行
  57. if (isFlipping.value) {
  58. isFlipping.value = false // 立即结束此次动画
  59. clearTimeout(timeoutID) // 清除上一个计时器任务
  60. await nextTick()
  61. await flip(front, back) // 开始最后一次翻牌任务
  62. return
  63. }
  64. // 设置翻盘前后数据
  65. backTextFromData.value = back
  66. frontTextFromData.value = front
  67. // 设置翻转状态为true
  68. isFlipping.value = true
  69. // 翻牌结束的行为
  70. timeoutID = setTimeout(() => {
  71. isFlipping.value = false // 设置翻转状态为false
  72. frontTextFromData.value = back
  73. }, props.duration)
  74. }
  75. watch(
  76. () => props.count,
  77. (newVal, oldVal) => {
  78. flip(oldVal as string | number, newVal as string | number)
  79. },
  80. {
  81. immediate: true
  82. }
  83. )
  84. </script>
  85. <style lang="scss" scoped>
  86. $frontColor: v-bind('props.frontColor');
  87. $backColor: v-bind('props.backColor');
  88. $radius: v-bind('`${props.radius}px`');
  89. $width: v-bind('`${props.width}px`');
  90. $height: v-bind('`${props.height}px`');
  91. $perspective: v-bind('`${props.height * 2}px`');
  92. $borderWidth: v-bind('`${props.borderWidth * 2}px`');
  93. $speed: v-bind('`${props.duration / 1000}s`');
  94. $shadowColor: #000000;
  95. $lineColor: #4a9ef8;
  96. // #region 动画效果
  97. @keyframes frontFlipDown {
  98. 0% {
  99. transform: perspective($perspective) rotateX(0deg);
  100. }
  101. 100% {
  102. transform: perspective($perspective) rotateX(-180deg);
  103. }
  104. }
  105. @keyframes backFlipDown {
  106. 0% {
  107. transform: perspective($perspective) rotateX(180deg);
  108. }
  109. 100% {
  110. transform: perspective($perspective) rotateX(0deg);
  111. }
  112. }
  113. @keyframes frontFlipUp {
  114. 0% {
  115. transform: perspective($perspective) rotateX(0deg);
  116. }
  117. 100% {
  118. transform: perspective($perspective) rotateX(180deg);
  119. }
  120. }
  121. @keyframes backFlipUp {
  122. 0% {
  123. transform: perspective($perspective) rotateX(-180deg);
  124. }
  125. 100% {
  126. transform: perspective($perspective) rotateX(0deg);
  127. }
  128. }
  129. // #endregion
  130. .go-flipper {
  131. display: inline-block;
  132. position: relative;
  133. width: $width;
  134. height: $height;
  135. line-height: $height;
  136. border-radius: $radius;
  137. background: $frontColor;
  138. font-size: $width;
  139. color: $frontColor;
  140. box-shadow: 0 0 6px rgba($color: $shadowColor, $alpha: 0.5); // 阴影部分
  141. text-align: center;
  142. // font-family: 'Helvetica Neue';
  143. &::after {
  144. content: '';
  145. position: absolute;
  146. z-index: 10;
  147. left: 0;
  148. top: 0;
  149. right: 0;
  150. bottom: 0;
  151. box-shadow: inset 0 0 $borderWidth 0 $frontColor; // 内测阴影部分
  152. border-radius: $radius;
  153. }
  154. .digital:before,
  155. .digital:after {
  156. content: '';
  157. position: absolute;
  158. left: 0;
  159. right: 0;
  160. background: $backColor;
  161. overflow: hidden;
  162. box-sizing: border-box;
  163. }
  164. .digital.front:before,
  165. .digital.front:after {
  166. content: attr(data-front) !important;
  167. }
  168. .digital.back:before,
  169. .digital.back:after {
  170. content: attr(data-back) !important;
  171. }
  172. .digital:before {
  173. top: 0;
  174. bottom: 50%;
  175. border-radius: $radius $radius 0 0;
  176. border-bottom: solid 1px rgba($color: $lineColor, $alpha: 0.3); // 中间线颜色
  177. }
  178. .digital:after {
  179. top: 50%;
  180. bottom: 0;
  181. border-radius: 0 0 $radius $radius;
  182. line-height: 0;
  183. }
  184. /*向下翻*/
  185. &.down .front:before {
  186. z-index: 3;
  187. }
  188. &.down .back:after {
  189. z-index: 2;
  190. transform-origin: 50% 0%;
  191. transform: perspective($perspective) rotateX(180deg);
  192. }
  193. &.down .front:after,
  194. &.down .back:before {
  195. z-index: 1;
  196. }
  197. &.down.go .front:before {
  198. transform-origin: 50% 100%;
  199. animation: frontFlipDown $speed ease-in-out both;
  200. box-shadow: 0 -2px $borderWidth 0 $frontColor;
  201. backface-visibility: hidden;
  202. }
  203. &.down.go .back:after {
  204. animation: backFlipDown $speed ease-in-out both;
  205. box-shadow: 0 2px $borderWidth 0 $frontColor;
  206. backface-visibility: hidden;
  207. }
  208. /*向上翻*/
  209. &.up .front:after {
  210. z-index: 3;
  211. }
  212. &.up .back:before {
  213. z-index: 2;
  214. transform-origin: 50% 100%;
  215. transform: perspective($perspective) rotateX(-180deg);
  216. }
  217. &.up .front:before,
  218. &.up .back:after {
  219. z-index: 1;
  220. }
  221. &.up.go .front:after {
  222. transform-origin: 50% 0;
  223. animation: frontFlipUp $speed ease-in-out both;
  224. box-shadow: 0 2px $borderWidth 0 $frontColor;
  225. backface-visibility: hidden;
  226. }
  227. &.up.go .back:before {
  228. animation: backFlipUp $speed ease-in-out both;
  229. box-shadow: 0 -2px $borderWidth 0 $frontColor;
  230. backface-visibility: hidden;
  231. }
  232. }
  233. </style>