vue+elementui實現(xiàn)拖住滑塊拼圖驗證
更新時間:2022年03月28日 14:50:28 作者:管火火火
這篇文章主要為大家詳細介紹了vue+elementui實現(xiàn)拖住滑塊拼圖驗證,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
vue拖住滑塊拼圖驗證,以下是cavas直接寫的滑塊拼圖驗證碼,直接復制引用即可
?<template> ??<div id="puzzle" ref="puzzle" style="display:inline-block;"> ??<!-- :style="'padding:' + 16*scale + 'px ' + 16*scale + 'px ' + 28*scale + 'px;border-radius:'+16*scale+'px;'" --> ????<div class="puzzle-container" ?????> ??????<div :style="'position:relative;overflow:hidden;width:'+ dataWidth +'px;'"> ????????<div :style="'position:relative;width:' + dataWidth + 'px;height:' + dataHeight + 'px;'"> ??????????<img ????????????id="scream" ????????????ref="scream" ????????????:src="imgRandom" ????????????:style="'width:' + dataWidth + 'px;height:' + dataHeight + 'px;'" ??????????/> ??????????<canvas id="puzzle-box" ref="puzzleBox" :width="dataWidth" :height="dataHeight"></canvas> ????????</div> ????????<div ??????????class="puzzle-lost-box" ??????????:style="'left:' + left_Num + 'px;width:' + dataWidth + 'px;height:' + dataHeight + 'px;'" ????????> ??????????<canvas id="puzzle-shadow" ref="puzzleShadow" :width="dataWidth" :height="dataHeight"></canvas> ??????????<canvas id="puzzle-lost" ref="puzzleLost" :width="dataWidth" :height="dataHeight"></canvas> ????????</div> ????????<p ??????????class="ver-tips" ??????????ref="verTips" ??????????:style="'height: '+22*scale+'px;line-height:'+22*scale+'px;bottom: ' + (displayTips==true ? 0 : -22*scale ) +'px;font-size: '+12*scale+'px;'" ????????> ??????????<template v-if="verification"> ????????????<span :style="'color:#42ca6b;line-height:'+ 22*scale+'px;'">驗證通過</span> ????????????<span :style="'margin-left:'+4*scale+'px;line-height:'+ 22*scale+'px;'">哇喔,怪物吃了拼圖,快去登錄吧~</span> ??????????</template> ??????????<template v-if="!verification"> ????????????<span :style="'color:red;line-height:'+22*scale+'px;'">驗證失敗:</span> ????????????<span :style="'margin-left:'+4*scale+'px;line-height:'+ 22*scale+'px;'">拖動滑塊將懸浮圖像正確拼合</span> ??????????</template> ????????</p> ??????</div> ??????<div ????????class="re-btn" ????????@click="refreshImg" ????????:style="'height: '+28*scale+'px;padding: '+1*scale+'px '+16*scale+'px;font-size: '+18*scale+'px;'" ??????> ????????<a-icon type="redo" /> ??????</div> ????</div> ????<br /> ????<div class="slider-container" :style="'width:' + dataWidth + 'px;'"> ??????<div class="slider-bar" :style="'border-radius:'+ 24*scale+'px;'"> ????????<p ??????????class="slider-bar-text select-none" ??????????onselectstart="return false" ??????????:style="'line-height:' + 28*scale + 'px;font-size:'+12*scale+'px;'" ????????>按住滑塊, 拖動完成上方拼圖</p> ??????</div> ??????<div ????????class="slider-btn" ????????ref="sliderBtn" ????????@mousedown="startMove" ????????@touchstart="startMove" ????????:style="'top: '+ -7*scale + 'px;'" ??????> ????????<a-icon ??????????type="pause-circle" ??????????theme="twoTone" ??????????twoToneColor="#52c41a" ??????????:style="'font-size: '+44*scale+'px;'" ????????/> ????????<span class="slider-center-zzz">▍▍▍</span> ??????</div> ????</div> ??</div> </template> <script lang="js"> export default { ??name: 'puzzle', ??data () { ????return { ??????moveStart: '', ??????displayTips: false, ??????verification: false, ??????randomX: null, ??????randomY: null, ??????imgRandom: '', ??????left_Num: 0, ??????dataWidth: 404, ??????dataHeight: 90, ??????puzzleSize: 100, ??????deviation: 11, ??????scale: 1, ??????reTimer: null, ??????isleavePage: false ????} ??}, ??props: { ????width: { ??????type: [String, Number], ??????default: 340 ????}, ????height: { ??????type: [String, Number], ??????default: 120 ????}, ????cropImage: { ??????type: [Boolean], ??????default: false ????}, ????puzzleImgList: { ??????type: Array, ??????default: () => [ ????????'../../../static/img/a.png', ??????] ????}, ????PlSize: { ??????type: [String, Number], ??????default: 48 ????}, ????Deviation:{ ??????type: [String, Number], ??????default: 4 ????}, ????padding: { ??????type: [Number], ??????default: 20 ????}, ????onSuccess: { ??????type: Function, ??????default: () => { ????????alert('驗證成功') ??????} ????}, ????onError: { ??????type: Function, ??????default: () => { ????????alert('驗證失敗') ??????} ????} ??}, ??methods: { ????/* 刷新 */ ????RandomNum(Min, Max) { ??????let Range = Max - Min + 1 ??????let Rand = Math.random() ??????return Min + Math.floor(Rand * Range) ????}, ????refreshImg () { ??????let imgRandomNum = this.RandomNum(0, this.puzzleImgList.length-1) ??????this.imgRandom = this.puzzleImgList[imgRandomNum] ??????this.initCanvas() ????}, ????/* 畫布初始化 */ ????initCanvas () { ??????this.clearCanvas() ??????// let self = this ??????let w = this.dataWidth ??????let h = this.dataHeight ??????// let PL_Size = 48 ??????// 2019-02-12開始PL_Size使用可傳遞參數(shù) ??????let PL_Size = this.puzzleSize ??????let padding = 20 ??????let MinN_X = padding + PL_Size ??????let MaxN_X = w - padding - PL_Size - PL_Size / 6 ??????let MaxN_Y = padding ??????let MinN_Y = h - padding - PL_Size - PL_Size / 6 ??????this.randomX = this.RandomNum(MinN_X, MaxN_X) ??????this.randomY = this.RandomNum(MinN_Y, MaxN_Y) ??????let X = this.randomX ??????let Y = this.randomY ??????this.left_Num = -X + 10 ??????let d = PL_Size / 3 ??????let c = this.$refs.puzzleBox ??????let c_l = this.$refs.puzzleLost ??????let c_s = this.$refs.puzzleShadow ??????let ctx = c.getContext('2d') ??????let ctx_l = c_l.getContext('2d') ??????let ctx_s = c_s.getContext('2d') ??????ctx.globalCompositeOperation = 'xor' ??????ctx.shadowBlur = 10 ??????ctx.shadowColor = '#fff' ??????ctx.shadowOffsetX = 3 ??????ctx.shadowOffsetY = 3 ??????ctx.fillStyle = 'rgba(0,0,0,0.7)' ??????ctx.beginPath() ??????ctx.lineWidth = '1' ??????ctx.strokeStyle = 'rgba(0,0,0,0)' ??????ctx.moveTo(X, Y) ??????ctx.lineTo(X + d, Y) ??????ctx.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y) ??????ctx.lineTo(X + 3 * d, Y) ??????ctx.lineTo(X + 3 * d, Y + d) ??????ctx.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d) ??????ctx.lineTo(X + 3 * d, Y + 3 * d) ??????ctx.lineTo(X, Y + 3 * d) ??????ctx.closePath() ??????ctx.stroke() ??????ctx.fill() ??????let img = new Image() ??????img.src = this.imgRandom ??????img.onload = function () { ????????ctx_l.beginPath() ????????ctx_l.strokeStyle = 'rgba(0,0,0,0)' ????????ctx_l.moveTo(X, Y) ????????ctx_l.lineTo(X + d, Y) ????????ctx_l.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y) ????????ctx_l.lineTo(X + 3 * d, Y) ????????ctx_l.lineTo(X + 3 * d, Y + d) ????????ctx_l.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d) ????????ctx_l.lineTo(X + 3 * d, Y + 3 * d) ????????ctx_l.lineTo(X, Y + 3 * d) ????????ctx_l.closePath() ????????ctx_l.stroke() ????????ctx_l.shadowBlur = 10 ????????ctx_l.shadowColor = 'black' ????????ctx_l.clip() ????????ctx_l.drawImage(img, 0, 0, w, h) ??????} ??????ctx_s.beginPath() ??????ctx_s.lineWidth = '1' ??????ctx_s.strokeStyle = 'rgba(0,0,0,0)' ??????ctx_s.moveTo(X, Y) ??????ctx_s.lineTo(X + d, Y) ??????ctx_s.bezierCurveTo(X + d, Y - d, X + 2 * d, Y - d, X + 2 * d, Y) ??????ctx_s.lineTo(X + 3 * d, Y) ??????ctx_s.lineTo(X + 3 * d, Y + d) ??????ctx_s.bezierCurveTo(X + 2 * d, Y + d, X + 2 * d, Y + 2 * d, X + 3 * d, Y + 2 * d) ??????ctx_s.lineTo(X + 3 * d, Y + 3 * d) ??????ctx_s.lineTo(X, Y + 3 * d) ??????ctx_s.closePath() ??????ctx_s.stroke() ??????ctx_s.shadowBlur = 20 ??????ctx_s.shadowColor = 'black' ??????ctx_s.fill() ????}, ????/* 通過重置畫布尺寸清空畫布,這種方式更徹底 */ ????clearCanvas () { ??????let c = this.$refs.puzzleBox ??????let c_l = this.$refs.puzzleLost ??????let c_s = this.$refs.puzzleShadow ??????c.setAttribute('height', c.getAttribute('height')) ??????c_l.setAttribute('height', c.getAttribute('height')) ??????c_s.setAttribute('height', c.getAttribute('height')) ??????// c.height = c.height ??????// c_l.height = c_l.height ??????// c_s.height = c_s.height ????}, ????/* 按住滑塊后初始化移動監(jiān)聽,記錄初始位置 */ ????startMove (e) { ??????console.log(e) ??????e = e || window.event ??????this.$refs.sliderBtn.style.backgroundPosition = '0 -216px' ??????this.moveStart = e.pageX || e.targetTouches[0].pageX ??????this.addMouseMoveListener() ????}, ????/* 滑塊移動 */ ????moving (e) { ??????let self = this ??????e = e || window.event ??????let moveX = e.pageX || e.targetTouches[0].pageX ??????let d = moveX - self.moveStart ??????let w = self.dataWidth ??????let PL_Size = this.puzzleSize ??????let padding = 20 ??????if (self.moveStart === '') { ????????return '' ??????} ??????if (d < 0 || d > (w - padding - PL_Size)) { ????????return '' ??????} ??????self.$refs.sliderBtn.style.left = d + 'px' ??????self.$refs.sliderBtn.style.transition = 'inherit' ??????self.$refs.puzzleLost.style.left = d + 'px' ??????self.$refs.puzzleLost.style.transition = 'inherit' ??????self.$refs.puzzleShadow.style.left = d + 'px' ??????self.$refs.puzzleShadow.style.transition = 'inherit' ????}, ????/* 移動結(jié)束,驗證并回調(diào) */ ????moveEnd (e) { ??????let self = this ??????e = e || window.event ??????let moveEnd_X = (e.pageX || e.changedTouches[0].pageX) - self.moveStart ??????let ver_Num = self.randomX - 10 ??????let deviation = this.deviation ??????let Min_left = ver_Num - deviation ??????let Max_left = ver_Num + deviation ??????if (self.moveStart !== '') { ????????if (Max_left > moveEnd_X && moveEnd_X > Min_left) { ???????????? ??????????self.displayTips = true ??????????self.verification = true ??????????setTimeout(function () { ????????????if(self.isleavePage){ ??????????????return ????????????} ????????????self.displayTips = false ????????????self.initCanvas() ??????????}, 2000) ??????????/* 成功的回調(diào)函數(shù) */ ??????????self.onSuccess() ????????} else { ??????????self.displayTips = true ??????????self.verification = false ??????????setTimeout(function () { ????????????if(self.isleavePage){ ??????????????return ????????????} ????????????self.displayTips = false ??????????}, 2000) ??????????/* 失敗的回調(diào)函數(shù) */ ??????????self.onError() ????????} ??????} ??????if (typeof(self.$refs.sliderBtn) !== 'undefined' && typeof (self.$refs.puzzleLost) !== 'undefined' && typeof (self.$refs.puzzleShadow) !== 'undefined') { ????????self.reTimer = setTimeout(function () { ??????????if(self.isleavePage){ ????????????return ??????????} ??????????self.$refs.sliderBtn.style.left = 0 ??????????self.$refs.sliderBtn.style.transition = 'left 0.5s' ??????????self.$refs.puzzleLost.style.left = 0 ??????????self.$refs.puzzleLost.style.transition = 'left 0.5s' ??????????self.$refs.puzzleShadow.style.left = 0 ??????????self.$refs.puzzleShadow.style.transition = 'left 0.5s' ????????}, 1000) ????????self.$refs.sliderBtn.style.backgroundPosition = '0 -84px' ??????} ??????self.moveStart = '' ????}, ????/* 綁定滑塊移動與滑動結(jié)束,移動過程中鼠標可在綁定的區(qū)域 */ ????addMouseMoveListener () { ??????let self = this ??????document.addEventListener('mousemove', self.moving) ??????document.addEventListener('touchmove', self.moving) ??????document.addEventListener('mouseup', self.moveEnd) ??????document.addEventListener('touchend', self.moveEnd) ????} ??}, ??mounted () { ????let self = this ????this.dataWidth = this.$refs.puzzle.clientWidth*0.884 ????this.dataHeight = this.dataWidth*(this.height/this.width) ????this.scale = this.$refs.puzzle.clientWidth/260 ????this.puzzleSize = this.dataWidth*(this.PlSize/this.width) ????setTimeout(self.refreshImg,0) ??}, ??created () { ????let imgRandomNum = this.RandomNum(0, this.puzzleImgList.length - 1) ????this.imgRandom = this.puzzleImgList[imgRandomNum] ????this.puzzleSize = Number(this.PlSize) ????this.deviation = Number(this.Deviation) ???? ????// this.dataWidth = this.width ????// this.dataHeight = this.height ????// this.scale = this.width/260 ??}, ??watch: { ??}, ??beforeDestroy() { ????this.isleavePage = true ??} } </script> <style scoped> .slider-btn { ??position: absolute; ??width: 90px; ??height: 67px; ??left: 0; ??top: -7px; ??z-index: 12; ??cursor: pointer; ??background-position: 0 -84px; ??transition: inherit; ??background-color: #f4f7fe; ??border-radius: 45%; ??display: flex; } .slider-center-zzz { ??text-align: center; ??line-height: 67px; ??position: absolute; ??color: #53C300; ??height: 40px; ??width: 100%; } .ver-tips { ??position: absolute; ??left: 0; ??bottom: -22px; ??background: rgba(255, 255, 255, 0.9); ??height: 22px; ??line-height: 22px; ??font-size: 12px; ??width: 100%; ??margin: 0; ??text-align: left; ??padding: 0 8px; ??transition: all 0.4s; } .slider-tips { ??bottom: 0; } .ver-tips i { ??display: inline-block; ??width: 22px; ??height: 22px; ??vertical-align: top; ??background-position: -4px -1229px; } .ver-tips span { ??display: inline-block; ??vertical-align: top; ??line-height: 22px; ??color: #455; } .active-tips { ??display: block; } .hidden { ??display: none; } .re-btn { ??position: absolute; ??left: 0; ??bottom: 0; ??height: 28px; ??padding: 0 16px; } .re-btn a { ??display: inline-block; ??width: 14px; ??height: 14px; ??margin: 0px 0; ??background-position: 0 -1179px; ??cursor: pointer; } .re-btn a:hover { ??background-position: 0 -1193px; } .puzzle-container { ??position: relative; ??/* padding: 16px 16px 28px; */ ??/* border: 1px solid #ddd; */ ??background: #e0e1e3; ??/* border-radius: 16px; */ ??/* height: 83px; ?*/ ??/* width: 340px; */ } .slider-container { ??position: relative; ??margin: auto; } .slider-bar { ??border: 1px solid #c3c3c3; ??border-radius: 24px; ??background: #e0e1e3; ??box-shadow: 0 1px 1px rgba(12, 10, 10, 0.2) inset; } .slider-bar-text { ??font-size: 12px; ??color: #486c80; ??line-height: 28px; ??margin: 0; ??text-align: right; ??padding-right: 22px; } #puzzle-box { ??position: absolute; ??left: 0; ??top: 0; ??z-index: 22; } #puzzle-shadow { ??position: absolute; ??left: 0; ??top: 0; ??z-index: 22; } #puzzle-lost { ??position: absolute; ??left: 0; ??top: 0; ??z-index: 33; } .puzzle-lost-box { ??position: absolute; ??width: 260px; ??height: 116px; ??left: 0; ??top: 0; ??z-index: 111; } </style>
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
Vue中如何實現(xiàn)在線預覽word文件、excel文件
這篇文章主要介紹了Vue中如何實現(xiàn)在線預覽word文件、excel文件,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07vue2和vue3的v-if與v-for優(yōu)先級對比學習
這篇文章主要介紹了vue2和vue3的v-if與v-for優(yōu)先級對比學習,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-10-10vue項目前端微信JSAPI與外部H5支付相關實現(xiàn)過程及常見問題
這篇文章主要介紹了vue項目前端微信JSAPI與外部H5支付相關實現(xiàn)過程及常見問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-04-04從vue基礎開始創(chuàng)建一個簡單的增刪改查的實例代碼(推薦)
這篇文章主要介紹了從vue基礎開始創(chuàng)建一個簡單的增刪改查的實例代碼,需要的朋友參考下2018-02-02基于Vue3和Plotly.js實現(xiàn)交互式3D圖
這篇文章主要介紹了基于Vue3和Plotly.js實現(xiàn)交互式3D圖,本代碼旨在為數(shù)據(jù)可視化提供一個交互式圖表,允許用戶動態(tài)控制圖表中線條的顏色和可見性,此功能對于探索大型數(shù)據(jù)集或突出特定數(shù)據(jù)子集非常有用,需要的朋友可以參考下2024-07-07如何解決vuex在頁面刷新后數(shù)據(jù)被清除的問題
這篇文章主要介紹了如何解決vuex在頁面刷新后數(shù)據(jù)被清除的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05elementPlus?的el-select在提示框關閉時自動彈出的問題解決
這篇文章主要介紹了elementPlus?的el-select在提示框關閉時自動彈出閉時自動彈出的問題,主要問題就是因為filterable屬性,根本解決方案是選中的時候讓他失去焦點?el-select有一個visible-change事件,下拉框出現(xiàn)/隱藏時觸發(fā),感興趣的朋友跟隨小編一起看看吧2024-01-01