vue实现图片滑动验证

Psyche ·
更新时间:2024-09-21
· 1365 次阅读

本文实例为大家分享了vue实现图片滑动验证的具体代码,供大家参考,具体内容如下

效果图:

1、引用自定义组件

import img0 from '../assets/img.jpg'; import img1 from '../assets/img1.jpg'; import img2 from '../assets/img2.jpg'; import img3 from '../assets/img3.jpg'; import img4 from '../assets/img4.jpg'; import img5 from '../assets/img5.jpg'; import solide from "../conponents/phptoSlide" components: {     solide   },

2、使用

<solide ref="slideblock" @success="onSuccess" @again="onAgain" @fulfilled="onFulfilled"    @fail="onFail" @refresh="onRefresh" :slider-text="text" :imgs="imgs" :accuracy="accuracy">   </solide> data () {     return {       msg: '',       text: '向右滑动->',       imgs: [         img0,         img1,         img2,         img3,         img4,         img5,       ],       loginBtn: false,       accuracy: 1, // 精确度小,可允许的误差范围小;为1时,则表示滑块要与凹槽完全重叠,才能验证成功。默认值为5       formLabelAlign: {},     }  }   methods: {     onSuccess (times) {       // console.log('验证通过');       this.loginBtn = true;       this.msg = `login success, 耗时${(times / 1000).toFixed(1)}s`;     },     onFail () {       // console.log('验证不通过');       this.msg = ''     },     onRefresh () {       // console.log('点击了刷新小图标');       this.msg = ''     },     onFulfilled () {       // console.log('刷新成功啦!');     },     onAgain () {       // console.log('检测到非人为操作的哦!');       this.msg = 'try again';       // 刷新       this.handleClick();     },     handleClick () {       this.$refs.slideblock.reset();       this.msg = ''     },   }

3、自定义组件文件

<template>   <div class="slide-verify" :style="{width: w + 'px'}" id="slideVerify" onselectstart="return false;">     <!-- 图片加载遮蔽罩 -->     <div :class="{'slider-verify-loading': loadBlock}"></div>     <canvas :width="w" :height="h" ref="canvas"></canvas>     <div v-if="show" @click="refresh" class="slide-verify-refresh-icon"></div>     <canvas :width="w" :height="h" ref="block" class="slide-verify-block"></canvas>     <!-- container -->     <div class="slide-verify-slider" :class="{'container-active': containerActive, 'container-success': containerSuccess, 'container-fail': containerFail}">       <div class="slide-verify-slider-mask" :style="{width: sliderMaskWidth}">         <!-- slider -->         <div @mousedown="sliderDown" @touchstart="touchStartEvent" @touchmove="touchMoveEvent" @touchend="touchEndEvent" class="slide-verify-slider-mask-item" :style="{left: sliderLeft}">           <div class="slide-verify-slider-mask-item-icon"></div>         </div>       </div>       <span class="slide-verify-slider-text">{{sliderText}}</span>     </div>   </div> </template> <script> const PI = Math.PI; function sum (x, y) {   return x + y } function square (x) {   return x * x } export default {   name: 'SlideVerify',   props: {     // block length     l: {       type: Number,       default: 30,     },     // block radius     r: {       type: Number,       default: 6,     },     // canvas width     w: {       type: Number,       default: 270,     },     // canvas height     h: {       type: Number,       default: 90,     },     sliderText: {       type: String,       default: 'Slide filled right',     },     accuracy: {       type: Number,       default: 5, // 若为 -1 则不进行机器判断     },     show: {       type: Boolean,       default: true,     },     imgs: {       type: Array,       default: () => [],     },   },   data () {     return {       containerActive: false, // container active class       containerSuccess: false, // container success class       containerFail: false, // container fail class       canvasCtx: null,       blockCtx: null,       block: null,       block_x: undefined, // container random position       block_y: undefined,       L: this.l + this.r * 2 + 3, // block real lenght       img: undefined,       originX: undefined,       originY: undefined,       isMouseDown: false,       trail: [],       sliderLeft: 0, // block right offset       sliderMaskWidth: 0, // mask width,       success: false, // Bug Fixes 修复了验证成功后还能滑动       loadBlock: true, // Features 图片加载提示,防止图片没加载完就开始验证       timestamp: null,     }   },   mounted () {     this.init()   },   methods: {     init () {       this.initDom()       this.initImg()       this.bindEvents()     },     initDom () {       this.block = this.$refs.block;       this.canvasCtx = this.$refs.canvas.getContext('2d')       this.blockCtx = this.block.getContext('2d')     },     initImg () {       const img = this.createImg(() => {         // 图片加载完关闭遮蔽罩         this.loadBlock = false;         this.drawBlock()         this.canvasCtx.drawImage(img, 0, 0, this.w, this.h)         this.blockCtx.drawImage(img, 0, 0, this.w, this.h)         let {           block_x: x,           block_y: y,           r,           L         } = this         let _y = y - r * 2 - 1         let ImageData = this.blockCtx.getImageData(x, _y, L, L);         this.block.width = L;         this.blockCtx.putImageData(ImageData, 0, _y)       });       this.img = img;     },     drawBlock () {       this.block_x = this.getRandomNumberByRange(this.L + 10, this.w - (this.L + 10))       this.block_y = this.getRandomNumberByRange(10 + this.r * 2, this.h - (this.L + 10))       this.draw(this.canvasCtx, this.block_x, this.block_y, 'fill')       this.draw(this.blockCtx, this.block_x, this.block_y, 'clip')     },     draw (ctx, x, y, operation) {       let {         l,         r       } = this;       ctx.beginPath()       ctx.moveTo(x, y)       ctx.arc(x + l / 2, y - r + 2, r, 0.72 * PI, 2.26 * PI)       ctx.lineTo(x + l, y)       ctx.arc(x + l + r - 2, y + l / 2, r, 1.21 * PI, 2.78 * PI)       ctx.lineTo(x + l, y + l)       ctx.lineTo(x, y + l)       ctx.arc(x + r - 2, y + l / 2, r + 0.4, 2.76 * PI, 1.24 * PI, true)       ctx.lineTo(x, y)       ctx.lineWidth = 2       ctx.fillStyle = 'rgba(255, 255, 255, 0.7)'       ctx.strokeStyle = 'rgba(255, 255, 255, 0.7)'       ctx.stroke()       ctx[operation]()       // Bug Fixes 修复了火狐和ie显示问题       ctx.globalCompositeOperation = "destination-over"     },     createImg (onload) {       const img = document.createElement('img');       img.crossOrigin = "Anonymous";       img.onload = onload;       img.onerror = () => {         img.src = this.getRandomImg()       }       img.src = this.getRandomImg()       return img;     },     // 随机生成img src     getRandomImg () {       // return require('../assets/img.jpg')       const len = this.imgs.length;       return len > 0 ?         this.imgs[this.getRandomNumberByRange(0, len)] :         'https://picsum.photos/300/150/?image=' + this.getRandomNumberByRange(0, 1084);     },     getRandomNumberByRange (start, end) {       return Math.round(Math.random() * (end - start) + start)     },     refresh () {       this.reset()       this.$emit('refresh')     },     sliderDown (event) {       if (this.success) return;       this.originX = event.clientX;       this.originY = event.clientY;       this.isMouseDown = true;       this.timestamp = + new Date();     },     touchStartEvent (e) {       if (this.success) return;       this.originX = e.changedTouches[0].pageX;       this.originY = e.changedTouches[0].pageY;       this.isMouseDown = true;       this.timestamp = + new Date();     },     bindEvents () {       document.addEventListener('mousemove', (e) => {         if (!this.isMouseDown) return false;         const moveX = e.clientX - this.originX;         const moveY = e.clientY - this.originY;         if (moveX < 0 || moveX + 38 >= this.w) return false;         this.sliderLeft = moveX + 'px';         let blockLeft = (this.w - 40 - 20) / (this.w - 40) * moveX;         this.block.style.left = blockLeft + 'px';         this.containerActive = true; // add active         this.sliderMaskWidth = moveX + 'px';         this.trail.push(moveY);       });       document.addEventListener('mouseup', (e) => {         if (!this.isMouseDown) return false         this.isMouseDown = false         if (e.clientX === this.originX) return false;         this.containerActive = false; // remove active         this.timestamp = + new Date() - this.timestamp;         const {           spliced,           TuringTest         } = this.verify();         if (spliced) {           if (this.accuracy === -1) {             this.containerSuccess = true;             this.success = true;             this.$emit('success', this.timestamp);             return;           }           if (TuringTest) {             // succ             this.containerSuccess = true;             this.success = true;             this.$emit('success', this.timestamp)           } else {             this.containerFail = true;             this.$emit('again')           }         } else {           this.containerFail = true;           this.$emit('fail')           setTimeout(() => {             this.reset()           }, 1000)         }       })     },     touchMoveEvent (e) {       if (!this.isMouseDown) return false;       const moveX = e.changedTouches[0].pageX - this.originX;       const moveY = e.changedTouches[0].pageY - this.originY;       if (moveX < 0 || moveX + 38 >= this.w) return false;       this.sliderLeft = moveX + 'px';       let blockLeft = (this.w - 40 - 20) / (this.w - 40) * moveX;       this.block.style.left = blockLeft + 'px';       this.containerActive = true;       this.sliderMaskWidth = moveX + 'px';       this.trail.push(moveY);     },     touchEndEvent (e) {       if (!this.isMouseDown) return false       this.isMouseDown = false       if (e.changedTouches[0].pageX === this.originX) return false;       this.containerActive = false;       this.timestamp = + new Date() - this.timestamp;       const {         spliced,         TuringTest       } = this.verify();       if (spliced) {         if (this.accuracy === -1) {           this.containerSuccess = true;           this.success = true;           this.$emit('success', this.timestamp);           return;         }         if (TuringTest) {           // succ           this.containerSuccess = true;           this.success = true;           this.$emit('success', this.timestamp)         } else {           this.containerFail = true;           this.$emit('again')         }       } else {         this.containerFail = true;         this.$emit('fail')         setTimeout(() => {           this.reset()         }, 1000)       }     },     verify () {       const arr = this.trail // drag y move distance       const average = arr.reduce(sum) / arr.length // average       const deviations = arr.map(x => x - average) // deviation array       const stddev = Math.sqrt(deviations.map(square).reduce(sum) / arr.length) // standard deviation       const left = parseInt(this.block.style.left)       const accuracy = this.accuracy <= 1 ? 1 : this.accuracy > 10 ? 10 : this.accuracy;       return {         spliced: Math.abs(left - this.block_x) <= accuracy,         TuringTest: average !== stddev, // equal => not person operate       }     },     reset () {       this.success = false;       this.containerActive = false;       this.containerSuccess = false;       this.containerFail = false;       this.sliderLeft = 0;       this.block.style.left = 0;       this.sliderMaskWidth = 0;       // canvas       let {         w,         h       } = this;       this.canvasCtx.clearRect(0, 0, w, h)       this.blockCtx.clearRect(0, 0, w, h)       this.block.width = w       // generate img       this.img.src = this.getRandomImg();       this.$emit('fulfilled')     },   } } </script> <style scoped> .slide-verify {   position: relative; } /* 图片加载样式 */ .slider-verify-loading {   position: absolute;   top: 0;   right: 0;   left: 0;   bottom: 0;   background: rgba(255, 255, 255, 0.9);   z-index: 999;   animation: loading 1.5s infinite; } @keyframes loading {   0% {     opacity: 0.7;   }   100% {     opacity: 9;   } } .slide-verify-block {   position: absolute;   left: 0;   top: 0; } .slide-verify-refresh-icon {   position: absolute;   right: 0;   top: 0;   width: 34px;   height: 34px;   cursor: pointer;   background: url("../assets/icon_light.png") 0 -437px;   background-size: 34px 471px; } .slide-verify-slider {   position: relative;   text-align: center;   width: 100%;   height: 40px;   line-height: 40px;   /* margin-top: 15px; */   background: #f7f9fa;   color: #45494c;   border: 1px solid #e4e7eb; } .slide-verify-slider-mask {   position: absolute;   left: 0;   top: 0;   height: 40px;   border: 0 solid #1991fa;   background: #d1e9fe; } .slide-verify-slider-mask-item {   position: absolute;   top: 0;   left: 0;   width: 40px;   height: 40px;   background: #fff;   box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);   cursor: pointer;   transition: background 0.2s linear; } .slide-verify-slider-mask-item:hover {   background: #1991fa; } .slide-verify-slider-mask-item:hover .slide-verify-slider-mask-item-icon {   background-position: 0 -13px; } .slide-verify-slider-mask-item-icon {   position: absolute;   top: 15px;   left: 13px;   width: 14px;   height: 12px;   background: url("../assets/icon_light.png") 0 -26px;   background-size: 34px 471px; } .container-active .slide-verify-slider-mask-item {   height: 38px;   top: -1px;   border: 1px solid #1991fa; } .container-active .slide-verify-slider-mask {   height: 38px;   border-width: 1px; } .container-success .slide-verify-slider-mask-item {   height: 38px;   top: -1px;   border: 1px solid #52ccba;   background-color: #52ccba !important; } .container-success .slide-verify-slider-mask {   height: 38px;   border: 1px solid #52ccba;   background-color: #d2f4ef; } .container-success .slide-verify-slider-mask-item-icon {   background-position: 0 0 !important; } .container-fail .slide-verify-slider-mask-item {   height: 38px;   top: -1px;   border: 1px solid #f57a7a;   background-color: #f57a7a !important; } .container-fail .slide-verify-slider-mask {   height: 38px;   border: 1px solid #f57a7a;   background-color: #fce1e1; } .container-fail .slide-verify-slider-mask-item-icon {   top: 14px;   background-position: 0 -82px !important; } .container-active .slide-verify-slider-text, .container-success .slide-verify-slider-text, .container-fail .slide-verify-slider-text {   display: none; } </style>



VUE 滑动验证 图片

需要 登录 后方可回复, 如果你还没有账号请 注册新账号