DragVerify.vue 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. <template>
  2. <!-- 滑动解锁插件 http://www.jq22.com/jquery-info22779 -->
  3. <div
  4. ref="dragVerify"
  5. class="drag_verify"
  6. :style="dragVerifyStyle"
  7. @mousemove="dragMoving"
  8. @mouseup="dragFinish"
  9. @mouseleave="dragFinish"
  10. @touchmove="dragMoving"
  11. @touchend="dragFinish"
  12. >
  13. <div class="dv_progress_bar" ref="progressBar" :class="{ goFirst2: isOk }" :style="progressBarStyle"></div>
  14. <div class="dv_text" :style="textStyle" ref="message">
  15. <slot name="textBefore" v-if="$slots.textBefore"></slot>
  16. {{ message }}
  17. <slot name="textAfter" v-if="$slots.textAfter"></slot>
  18. </div>
  19. <div
  20. class="dv_handler dv_handler_bg"
  21. :class="{ goFirst: isOk }"
  22. @mousedown="dragStart"
  23. @touchstart="dragStart"
  24. ref="handler"
  25. :style="handlerStyle"
  26. >
  27. <component :is="handlerIcon"></component>
  28. </div>
  29. </div>
  30. </template>
  31. <script setup>
  32. import { ref, computed, onMounted } from 'vue'
  33. import { CheckCircleOutlined } from '@ant-design/icons-vue'
  34. const props = defineProps({
  35. // 是否通过
  36. isPassing: {
  37. type: Boolean,
  38. default: false
  39. },
  40. // 宽度
  41. width: {
  42. type: Number,
  43. default: 250
  44. },
  45. // 高度
  46. height: {
  47. type: Number,
  48. default: 40
  49. },
  50. // 组件文案
  51. text: {
  52. type: String,
  53. default: 'swiping to the right side'
  54. },
  55. // 成功文案
  56. successText: {
  57. type: String,
  58. default: 'success'
  59. },
  60. // 背景色
  61. background: {
  62. type: String,
  63. default: '#eee'
  64. },
  65. // 解锁中背景色
  66. progressBarBg: {
  67. type: String,
  68. default: '#76c61d'
  69. },
  70. // 解锁成功背景色
  71. completedBg: {
  72. type: String,
  73. default: '#76c61d'
  74. },
  75. circle: {
  76. type: Boolean,
  77. default: false
  78. },
  79. radius: {
  80. type: String,
  81. default: '4px'
  82. },
  83. handlerIcon: {
  84. type: String
  85. },
  86. successIcon: {
  87. type: String
  88. },
  89. handlerBg: {
  90. type: String,
  91. default: '#fff'
  92. },
  93. textSize: {
  94. type: String,
  95. default: '14px'
  96. },
  97. textColor: {
  98. type: String,
  99. default: '#333'
  100. }
  101. })
  102. const emit = defineEmits(['update:isPassing', 'handlerMove', 'passcallback'])
  103. const dragVerify = ref(null)
  104. const progressBar = ref(null)
  105. const messageRef = ref(null)
  106. const handler = ref(null)
  107. const isMoving = ref(false)
  108. const x = ref(0)
  109. const isOk = ref(false)
  110. onMounted(() => {
  111. if (dragVerify.value) {
  112. dragVerify.value.style.setProperty('--textColor', props.textColor)
  113. dragVerify.value.style.setProperty('--width', Math.floor(props.width / 2) + 'px')
  114. dragVerify.value.style.setProperty('--pwidth', -Math.floor(props.width / 2) + 'px')
  115. }
  116. })
  117. const handlerStyle = computed(() => {
  118. return {
  119. left: '0px',
  120. width: props.height + 'px',
  121. height: props.height + 'px',
  122. background: props.handlerBg
  123. }
  124. })
  125. const message = computed(() => {
  126. return props.isPassing ? props.successText : props.text
  127. })
  128. const dragVerifyStyle = computed(() => {
  129. return {
  130. width: props.width + 'px',
  131. height: props.height + 'px',
  132. lineHeight: props.height + 'px',
  133. background: props.background,
  134. borderRadius: props.circle ? props.height / 2 + 'px' : props.radius
  135. }
  136. })
  137. const progressBarStyle = computed(() => {
  138. return {
  139. background: props.progressBarBg,
  140. height: props.height + 'px',
  141. borderRadius: props.circle ? props.height / 2 + 'px 0 0 ' + props.height / 2 + 'px' : props.radius
  142. }
  143. })
  144. const textStyle = computed(() => {
  145. return {
  146. height: props.height + 'px',
  147. width: props.width + 'px',
  148. fontSize: props.textSize
  149. }
  150. })
  151. const dragStart = (e) => {
  152. if (!props.isPassing) {
  153. isMoving.value = true
  154. const handlerEl = handler.value
  155. x.value = (e.pageX || e.touches[0].pageX) - parseInt(handlerEl.style.left.replace('px', ''), 10)
  156. }
  157. emit('handlerMove')
  158. }
  159. const dragMoving = (e) => {
  160. if (isMoving.value && !props.isPassing) {
  161. let _x = (e.pageX || e.touches[0].pageX) - x.value
  162. const handlerEl = handler.value
  163. const progressBarEl = progressBar.value
  164. if (_x > 0 && _x <= props.width - props.height) {
  165. handlerEl.style.left = _x + 'px'
  166. progressBarEl.style.width = _x + props.height / 2 + 'px'
  167. } else if (_x > props.width - props.height) {
  168. handlerEl.style.left = props.width - props.height + 'px'
  169. progressBarEl.style.width = props.width - props.height / 2 + 'px'
  170. passVerify()
  171. }
  172. }
  173. }
  174. const dragFinish = (e) => {
  175. if (isMoving.value && !props.isPassing) {
  176. let _x = (e.pageX || e.changedTouches[0].pageX) - x.value
  177. if (_x < props.width - props.height) {
  178. isOk.value = true
  179. setTimeout(() => {
  180. handler.value.style.left = '0'
  181. progressBar.value.style.width = '0'
  182. isOk.value = false
  183. }, 500)
  184. } else {
  185. const handlerEl = handler.value
  186. handlerEl.style.left = props.width - props.height + 'px'
  187. progressBar.value.style.width = props.width - props.height / 2 + 'px'
  188. passVerify()
  189. }
  190. isMoving.value = false
  191. }
  192. }
  193. const passVerify = () => {
  194. emit('update:isPassing', true)
  195. isMoving.value = false
  196. const handlerEl = handler.value
  197. const messageEl = messageRef.value
  198. if (handlerEl && handlerEl.children[0]) {
  199. handlerEl.children[0].className = props.successIcon
  200. }
  201. if (progressBar.value) {
  202. progressBar.value.style.background = props.completedBg
  203. }
  204. if (messageEl) {
  205. messageEl.style['-webkit-text-fill-color'] = 'unset'
  206. messageEl.style.animation = 'slidetounlock2 3s infinite'
  207. messageEl.style.color = '#fff'
  208. }
  209. emit('passcallback')
  210. }
  211. const reset = () => {
  212. isMoving.value = false
  213. x.value = 0
  214. isOk.value = false
  215. const handlerEl = handler.value
  216. const messageEl = messageRef.value
  217. if (handlerEl) {
  218. handlerEl.style.left = '0'
  219. if (handlerEl.children[0]) {
  220. handlerEl.children[0].className = props.handlerIcon
  221. }
  222. }
  223. if (progressBar.value) {
  224. progressBar.value.style.width = '0'
  225. }
  226. if (messageEl) {
  227. messageEl.style['-webkit-text-fill-color'] = 'transparent'
  228. messageEl.style.animation = 'slidetounlock 3s infinite'
  229. messageEl.style.color = props.background
  230. }
  231. }
  232. </script>
  233. <style lang="less" scoped>
  234. .drag_verify {
  235. position: relative;
  236. background-color: #e8e8e8;
  237. text-align: center;
  238. overflow: hidden;
  239. .dv_handler {
  240. position: absolute;
  241. top: 0px;
  242. left: 0px;
  243. cursor: move;
  244. i {
  245. color: #666;
  246. padding-left: 0;
  247. font-size: 16px;
  248. }
  249. .anticon-check-circle {
  250. color: #6c6;
  251. margin-top: 9px;
  252. }
  253. }
  254. .dv_progress_bar {
  255. position: absolute;
  256. height: 34px;
  257. width: 0px;
  258. }
  259. .dv_text {
  260. position: absolute;
  261. top: 0px;
  262. color: transparent;
  263. -moz-user-select: none;
  264. -webkit-user-select: none;
  265. user-select: none;
  266. -o-user-select: none;
  267. -ms-user-select: none;
  268. background: -webkit-gradient(
  269. linear,
  270. left top,
  271. right top,
  272. color-stop(0, var(--textColor)),
  273. color-stop(0.4, var(--textColor)),
  274. color-stop(0.5, #fff),
  275. color-stop(0.6, var(--textColor)),
  276. color-stop(1, var(--textColor))
  277. );
  278. -webkit-background-clip: text;
  279. -webkit-text-fill-color: transparent;
  280. -webkit-text-size-adjust: none;
  281. animation: slidetounlock 3s infinite;
  282. * {
  283. -webkit-text-fill-color: var(--textColor);
  284. }
  285. }
  286. .goFirst {
  287. left: 0px !important;
  288. transition: left 0.5s;
  289. }
  290. .goFirst2 {
  291. width: 0px !important;
  292. transition: width 0.5s;
  293. }
  294. }
  295. </style>
  296. <style>
  297. @keyframes slidetounlock {
  298. 0% {
  299. background-position: var(--pwidth) 0;
  300. }
  301. 100% {
  302. background-position: var(--width) 0;
  303. }
  304. }
  305. @keyframes slidetounlock2 {
  306. 0% {
  307. background-position: var(--pwidth) 0;
  308. }
  309. 100% {
  310. background-position: var(--pwidth) 0;
  311. }
  312. }
  313. </style>