index.vue 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. <!-- @format -->
  2. <script lang="ts" setup>
  3. import { Check, DArrowRight } from '@element-plus/icons-vue'
  4. defineProps({
  5. // 成功图标
  6. successIcon: {
  7. type: String,
  8. default: 'iconfont icon-status-nor',
  9. },
  10. // 成功文字
  11. successText: {
  12. type: String,
  13. default: '验证成功',
  14. },
  15. // 开始的图标
  16. startIcon: {
  17. type: String,
  18. default: 'iconfont icon-login-slid',
  19. },
  20. // 开始的文字
  21. startText: {
  22. type: String,
  23. default: '拖动滑块到最右边',
  24. },
  25. })
  26. const modelValue = defineModel('value')
  27. const verifyResult = ref(false) // 验证结果
  28. const isTouch = (function () {
  29. if (typeof document === 'undefined' || !document.documentElement)
  30. return false
  31. return 'ontouchstart' in document.documentElement
  32. })()
  33. const moveEvent = isTouch ? 'touchmove' : 'mousemove'
  34. const upEvent = isTouch ? 'touchend' : 'mouseup'
  35. // 滑块触摸开始
  36. function onStart(ev: MouseEvent | TouchEvent) {
  37. let disX = 0 // 滑块移动距离
  38. const iconWidth = 40 // 滑动图标宽度
  39. const ele = document.querySelector('.drag-verify .block') as HTMLElement
  40. const startX
  41. = (ev as MouseEvent).clientX || (ev as TouchEvent).touches[0].pageX
  42. const parentWidth = ele.offsetWidth
  43. const MaxX = parentWidth - iconWidth
  44. if (verifyResult.value)
  45. return false
  46. // 滑块触摸移动
  47. const onMove = (e: MouseEvent | TouchEvent) => {
  48. const endX = (e as MouseEvent).clientX || (e as TouchEvent).touches[0].pageX
  49. disX = endX - startX
  50. if (disX <= 0)
  51. disX = 0
  52. if (disX >= MaxX - iconWidth)
  53. disX = MaxX
  54. ele.style.transition = '.1s all'
  55. ele.style.transform = `translateX(${disX}px)`
  56. }
  57. // 滑块触摸结束
  58. const onEnd = () => {
  59. if (disX !== MaxX) {
  60. ele.style.transition = '.5s all'
  61. ele.style.transform = 'translateX(0)'
  62. }
  63. else {
  64. // 执行成功
  65. verifyResult.value = true
  66. // emit('update:value', verifyResult.value)
  67. modelValue.value = verifyResult.value
  68. }
  69. document.removeEventListener(moveEvent, onMove)
  70. document.removeEventListener(upEvent, onEnd)
  71. }
  72. document.addEventListener(moveEvent, onMove)
  73. document.addEventListener(upEvent, onEnd)
  74. }
  75. </script>
  76. <template>
  77. <div class="drag-verify">
  78. <div class="range" :class="verifyResult ? 'success' : ''">
  79. <div class="block" @mousedown="onStart" @touchstart="onStart">
  80. <el-icon v-if="verifyResult" class="icon">
  81. <Check />
  82. </el-icon>
  83. <el-icon v-else class="icon">
  84. <DArrowRight />
  85. </el-icon>
  86. </div>
  87. <span class="text">{{ verifyResult ? successText : startText }}</span>
  88. </div>
  89. </div>
  90. </template>
  91. <style lang="less" scoped>
  92. @primary-color: #CC9879;
  93. .drag-verify {
  94. width: 100%;
  95. .range {
  96. background-color: #ececee;
  97. position: relative;
  98. border-radius: 4px;
  99. transition: 1s all;
  100. user-select: none;
  101. color: #666;
  102. overflow: hidden;
  103. display: flex;
  104. justify-content: center;
  105. align-items: center;
  106. height: 40px;
  107. &.success {
  108. background-color: @primary-color;
  109. color: #fff;
  110. .text {
  111. position: relative;
  112. z-index: 1;
  113. }
  114. .block .icon {
  115. color: @primary-color;
  116. }
  117. }
  118. .block {
  119. display: block;
  120. position: absolute;
  121. left: calc(-100% + 40px);
  122. width: 100%;
  123. height: 100%;
  124. background: @primary-color;
  125. border-radius: 4px;
  126. overflow: hidden;
  127. .icon {
  128. position: absolute;
  129. right: 0;
  130. width: 40px;
  131. height: 100%;
  132. font-size: 20px;
  133. color: #c8c9cc;
  134. background-color: #fff;
  135. border: 1px solid #e5e5e5;
  136. border-radius: 4px;
  137. cursor: pointer;
  138. display: flex;
  139. justify-content: center;
  140. align-items: center;
  141. }
  142. }
  143. }
  144. }
  145. </style>