slider-verify.vue 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. <template>
  2. <uni-popup ref="popupRef" @change="change">
  3. <view class="popup-box">
  4. <view class="verifyBox">
  5. <view class="verify-title">
  6. <text>安全验证</text>
  7. <text @click.stop="$refs.popupRef.close()" class="verify-close iconfont">&#xeca0;</text>
  8. </view>
  9. <view class="verify-hint">
  10. 滑动下方滑块完成拼图
  11. </view>
  12. <view class="slide-content">
  13. <view class="slider-pintu">
  14. <image id="pintuImg" :src="pintuImgs[pintuIndex]" class="pintu">
  15. </image>
  16. <!-- <image id="pintuImg" :src="'@/static/images/slider-verify/' + img + '.png'" class="pintu">
  17. </image> -->
  18. <view class="pintukuai" :style="{ top: top + 'px', left: oldx + 'px' }">
  19. <image :src="pintuImgs[pintuIndex]"
  20. :style="{ top: '-' + top + 'px', left: '-' + left + 'px'}">
  21. </image>
  22. </view>
  23. <view class="yinying" :style="{ top: top + 'px', left: left + 'px' }"></view>
  24. <view class="slider-success" v-show="moveSuccess">
  25. <text class="slider-success-icon iconfont">&#xe627;</text>
  26. <text class="slider-success-text">只用了{{ moveTime }}S,简直比闪电还快</text>
  27. </view>
  28. </view>
  29. <view class="slider-movearea" @touchend="endTouchMove">
  30. <movable-area :animation="true">
  31. <movable-view :x="x" direction="horizontal" @change="startMove">
  32. <text class="movable-item"></text>
  33. <text class="movable-item"></text>
  34. <text class="movable-item"></text>
  35. </movable-view>
  36. </movable-area>
  37. <view class="huadao"></view>
  38. </view>
  39. </view>
  40. <view class="slider-btn-group">
  41. <text class="slider-err"> {{ sliderErrText }} </text>
  42. <view class="slide-btn-refresh iconfont" @tap="refreshVerify">&#xec08;</view>
  43. </view>
  44. </view>
  45. </view>
  46. </uni-popup>
  47. </template>
  48. <script>
  49. export default {
  50. data() {
  51. return {
  52. pintuImgs: [
  53. require('@/static/images/login/img_01.jpg'),
  54. require('@/static/images/login/img_02.jpg'),
  55. require('@/static/images/login/img_03.jpg'),
  56. ],
  57. pintuIndex: 0,
  58. x: 0, //初始距离
  59. oldx: 0, //移动的距离
  60. img: 'logo', //显示哪张图片
  61. left: 0, //随机拼图的最终X轴距离
  62. top: 0, //拼图的top距离
  63. moveTime: 0,
  64. moveInterval: null,
  65. moveSuccess: false,
  66. sliderErrText: '',
  67. };
  68. },
  69. mounted() {
  70. // this.open();
  71. },
  72. methods: {
  73. open() {
  74. this.refreshVerify();
  75. this.$nextTick(() => {
  76. this.$refs.popupRef.open();
  77. })
  78. },
  79. setPintuIndex() {
  80. const n = Math.random() * 10;
  81. if (n < 3) {
  82. this.pintuIndex = 0
  83. } else if (n >= 3 && n <= 6) {
  84. this.pintuIndex = 1
  85. } else {
  86. this.pintuIndex = 2
  87. };
  88. },
  89. //刷新验证
  90. refreshVerify() {
  91. this.setPintuIndex()
  92. this.moveSuccess = false;
  93. this.sliderErrText = '';
  94. this.clearMoveTime()
  95. var gl = Math.random().toFixed(2);
  96. const leftNum = uni.upx2px(560) * gl > uni.upx2px(280) ? uni.upx2px(280) : uni.upx2px(560) * gl + uni
  97. .upx2px(
  98. 150); //生成随机X轴最终距离
  99. this.top = uni.upx2px(190) * gl; //生成随机Y轴初始距离
  100. const maxLeft = uni.upx2px(560) - uni.upx2px(90) - uni.upx2px(70)
  101. this.left = leftNum > maxLeft ? maxLeft : leftNum
  102. if (gl <= 0.2) {
  103. this.img = 1;
  104. }
  105. if (gl > 0.2 && gl <= 0.4) {
  106. this.img = 2;
  107. }
  108. if (gl > 0.4 && gl <= 0.6) {
  109. this.img = 3;
  110. }
  111. if (gl > 0.6 && gl <= 0.8) {
  112. this.img = 4;
  113. }
  114. if (gl > 0.8 && gl <= 1) {
  115. this.img = 5;
  116. }
  117. this.resetMove(); //重置阴影位置
  118. },
  119. /* 滑动中 */
  120. startMove(e) {
  121. if (e.detail.source) {
  122. this.setMoveTime();
  123. const maxNum = uni.upx2px(560) - uni.upx2px(70)
  124. if (e.detail.x <= maxNum) {
  125. this.oldx = e.detail.x;
  126. }
  127. };
  128. },
  129. setMoveTime(clear = false) {
  130. if (!this.moveInterval) {
  131. this.moveTime = 0;
  132. this.moveInterval = setInterval(() => {
  133. this.moveTime = Math.floor((this.moveTime + 0.1) * 100) / 100;
  134. // console.log('this.moveTime = ', this.moveTime)
  135. }, 100)
  136. }
  137. if (clear) {
  138. try {
  139. setTimeout(() => {
  140. clearInterval(this.moveInterval)
  141. this.moveInterval = null;
  142. }, 100)
  143. // console.log('this.moveInterval 1= ', this.moveInterval)
  144. // console.log('this.moveInterval 2= ', this.moveInterval)
  145. } catch {}
  146. }
  147. },
  148. clearMoveTime() {
  149. this.setMoveTime(true);
  150. },
  151. change(e) {
  152. this.$emit('change', e.show)
  153. console.log('change = ', e.show)
  154. },
  155. /* 滑动结束 */
  156. endTouchMove() {
  157. var that = this;
  158. this.clearMoveTime()
  159. if (Math.abs(that.oldx - that.left) <= 5) {
  160. // uni.showToast({
  161. // title: '验证成功',
  162. // duration: 2500,
  163. // success() {
  164. // that.$emit('touchSliderResult', true);
  165. // }
  166. // });
  167. this.moveSuccess = true;
  168. setTimeout(() => {
  169. this.$refs.popupRef.close();
  170. this.$emit('slideImgSuccess')
  171. }, 1000)
  172. } else {
  173. this.sliderErrText = '请控制拼图对齐切口'
  174. setTimeout(() => {
  175. this.sliderErrText = ''
  176. that.refreshVerify();
  177. }, 1000)
  178. }
  179. },
  180. /* 重置阴影位置 */
  181. resetMove() {
  182. this.x = 1;
  183. this.oldx = 1;
  184. setTimeout(() => {
  185. this.x = 0;
  186. this.oldx = 0;
  187. }, 300);
  188. },
  189. // 关闭
  190. closeSlider() {
  191. this.$emit('touchSliderResult', false);
  192. },
  193. }
  194. }
  195. </script>
  196. <style lang="scss" scoped>
  197. $imgSize: 80rpx;
  198. $pintuH: 360rpx;
  199. .verifyBox {
  200. // position: absolute;
  201. // top: 50%;
  202. // left: 50%;
  203. // transform: translate(-50%, -50%);
  204. // width: 560rpx;
  205. background-color: #fff;
  206. border-radius: 4rpx;
  207. padding: 8px;
  208. // box-shadow: 0 0 5upx rgba(0, 0, 0);
  209. .verify-title {
  210. width: 100%;
  211. display: flex;
  212. align-items: center;
  213. justify-content: space-between;
  214. font-size: 20rpx;
  215. color: #ccc;
  216. .verify-close {
  217. font-size: 40rpx;
  218. color: #666;
  219. }
  220. }
  221. .verify-hint {
  222. font-size: 28rpx;
  223. }
  224. .slide-content {
  225. width: 100%;
  226. padding: 5px 0;
  227. margin: 0 auto;
  228. .slide-tips {
  229. font-size: 28rpx;
  230. color: rgba(2, 20, 33, 0.45);
  231. padding: 0.5em 0;
  232. }
  233. .slider-pintu {
  234. position: relative;
  235. width: 100%;
  236. border-radius: 10rpx;
  237. overflow: hidden;
  238. .pintu {
  239. width: 560rpx;
  240. height: $pintuH;
  241. display: block;
  242. margin: 0 auto;
  243. }
  244. .pintukuai {
  245. position: absolute;
  246. top: 0;
  247. left: 0;
  248. width: $imgSize;
  249. height: $imgSize;
  250. z-index: 100;
  251. box-shadow: 0 0 5upx rgba(0, 0, 0, 0.3);
  252. overflow: hidden;
  253. image {
  254. display: block;
  255. position: absolute;
  256. top: 0;
  257. left: 0;
  258. width: 560rpx;
  259. height: $pintuH;
  260. }
  261. }
  262. }
  263. .yinying {
  264. position: absolute;
  265. width: $imgSize;
  266. height: $imgSize;
  267. background-color: rgba(0, 0, 0, 0.5);
  268. }
  269. // <view class="slider-success">
  270. // <text class="slider-success-icon">&#xe627;</text>
  271. // <text class="slider-success-text">只用了2S,</text>
  272. // </view>
  273. .slider-success {
  274. width: 100%;
  275. height: 100%;
  276. position: absolute;
  277. z-index: 101;
  278. left: 0;
  279. top: 0;
  280. background-color: rgba(255, 255, 255, 0.8);
  281. display: flex;
  282. justify-content: center;
  283. align-items: center;
  284. flex-direction: column;
  285. .slider-success-icon {
  286. font-size: 80rpx;
  287. color: $Theme-Color;
  288. }
  289. .slider-success-text {
  290. padding-top: 20rpx;
  291. font-size: 26rpx;
  292. color: $Theme-Color;
  293. }
  294. }
  295. }
  296. }
  297. .slider-movearea {
  298. position: relative;
  299. height: 50rpx;
  300. width: 100%;
  301. margin: 18rpx auto;
  302. movable-area {
  303. height: 50rpx;
  304. width: calc(100% - 90rpx);
  305. movable-view {
  306. width: 90rpx;
  307. height: 50rpx;
  308. border-radius: 25rpx;
  309. background-color: #007cff;
  310. position: relative;
  311. z-index: 100;
  312. box-shadow: 0px 0px 10px -1px #007cff;
  313. display: flex;
  314. justify-content: center;
  315. align-items: center;
  316. .movable-item {
  317. width: 3rpx;
  318. height: 20rpx;
  319. background-color: #fff;
  320. margin-right: 4px;
  321. &:last-child {
  322. margin-right: 0;
  323. }
  324. }
  325. }
  326. }
  327. }
  328. .huadao {
  329. width: 100%;
  330. height: 22rpx;
  331. background: #c2c2c2;
  332. box-shadow: inset 0 0 5upx #ccc;
  333. border-radius: 40rpx;
  334. position: absolute;
  335. top: 50%;
  336. left: 0;
  337. transform: translateY(-50%);
  338. font-size: 28rpx;
  339. z-index: 99;
  340. }
  341. .slider-btn-group {
  342. width: 100%;
  343. display: flex;
  344. justify-content: space-between;
  345. align-items: center;
  346. .slider-err {
  347. font-size: 24rpx;
  348. color: red;
  349. }
  350. .slide-btn-refresh {
  351. font-size: 40rpx;
  352. color: #c1c1c1;
  353. }
  354. }
  355. </style>