您好,登錄后才能下訂單哦!
緣由:之前看嗶哩嗶哩官網登錄的時候有一個拼圖驗證碼,很好奇怎么去實現。然后就想著自己弄一個。先給大家看我的最終效果。后面再一點點拆解代碼。
為什么想著寫這個功能呢,主要在于拼圖驗證碼在前端這里會比較復雜并且深入。相比文字拼寫,12306的圖片驗證碼都沒有拼圖驗證碼對前端的要求來的復雜,和難。
我總結下知識點:
1、彈窗功能
2、彈窗基于元素定位
3、元素拖動
4、canvas繪圖
5、基礎邏輯
一、彈窗和彈窗組件
抱歉,這里我偷懶了直接用了elementUI的el-popover組件,所以小伙伴不懂的直接看elementUI官網的說明。
我個人也研究和編寫了這塊的組件功能(基于popper.js)
二、編寫基礎結構
這塊屬于html的基礎內容,也就標題黨了
三、canvas繪制圖片
1、canvas繪制外部img圖片
代碼:
let?mainDom?=?document.querySelector("#codeImg");let?bg?=?mainDom.getContext("2d");let?width?=?mainDom.width;let?height?=?mainDom.height;let?blockDom?=?document.querySelector("#sliderBlock");let?block?=?blockDom.getContext("2d"); //重新賦值,讓canvas進行重新繪制 blockDom.height?=?height; mainDom.height?=?height;let?imgsrc?=?require("../assets/images/back.jpg");let?img?=?document.createElement("img"); img.style.objectFit?=?"scale-down"; img.src?=?imgsrc; img.onload?=?function()?{ ?bg.drawImage(img,?0,?0,?width,?height);?block.drawImage(img,?0,?0,?width,?height); };復制代碼
這里我繪制了兩個canvas,因為一個是背景一個是滑塊
核心在于
let?mainDom?=?document.querySelector("#codeImg");let?imgsrc?=?require("../assets/images/back.jpg");let?bg?=?mainDom.getContext("2d");let?img?=?document.createElement("img"); img.onload?=?function()?{ ?bg.drawImage(img,?0,?0,?width,?height); }; 復制代碼
2、canvas繪制滑塊部分
就是這個圖,這個有一些知識點,不難,但是很復雜。
代碼部分:
drawBlock(ctx,?xy?=?{?x:?254,?y:?109,?r:?9?},?type)?{ ?let?x?=?xy.x, ?y?=?xy.y, ?r?=?xy.r, ?w?=?40; ?let?PI?=?Math.PI;?//繪制 ?ctx.beginPath();?//left ?ctx.moveTo(x,?y);?//top ?ctx.arc(x?+?(w?+?5)?/?2,?y,?r,?-PI,?0,?true); ?ctx.lineTo(x?+?w?+?5,?y);?//right ?ctx.arc(x?+?w?+?5,?y?+?w?/?2,?r,?1.5?*?PI,?0.5?*?PI,?false); ?ctx.lineTo(x?+?w?+?5,?y?+?w);?//bottom ?ctx.arc(x?+?(w?+?5)?/?2,?y?+?w,?r,?0,?PI,?false); ?ctx.lineTo(x,?y?+?w); ?ctx.arc(x,?y?+?w?/?2,?r,?0.5?*?PI,?1.5?*?PI,?true); ?ctx.lineTo(x,?y);?//修飾,沒有會看不出效果 ?ctx.lineWidth?=?1; ?ctx.fillStyle?=?"rgba(255,?255,?255,?0.5)"; ?ctx.strokeStyle?=?"rgba(255,?255,?255,?0.5)"; ?ctx.stroke(); ?ctx[type](); ?ctx.globalCompositeOperation?=?"xor"; }復制代碼
解釋下:
參數是傳入canvas對象
x,y軸數據
剪切還是填充的canvas函數(fill,clip)
繪制難點:(很重要,不然你沒法理解它怎么繪制的)
繪制主要是需要理解這里的繪制是根據你設置一個起始點坐標,然后你繪制第二次的時候線就會連接到第二個點,依次連接最后回到原點就形成一個完整的圖形。
用的是arc參數,主要是看這個圖
fill是用于填充繪制的部分,clip是裁剪出繪制的部分,利用這個就可以出現一個扣掉的圖片和一個裁剪出來的圖片。
完成之后就是我的那個函數了。大家可以直接拿去用。
3、讓元素跟隨鼠標點擊之后滑動
這里其實原理非常簡單,就是有一個注意點。
原理:
鼠標點擊之后記錄當前坐標,然后隨著(mousemove)滾動的時候修改元素的left和top值就行了。
還有一點就是鼠標快速滑動會導致丟失滑動效果,這里需要用document,不能是元素級別的監聽。
元素上面我只需要鑒定按下mousedown
代碼:
//鼠標按下drag(e)?{?console.log("鼠標按下",?e);?let?dom?=?e.target;?//dom元素 ?let?slider?=?document.querySelector("#sliderBlock");?//滑塊dom ?const?downCoordinate?=?{?x:?e.x,?y:?e.y?};?//正確的滑塊數據 ?let?checkx?=?Number(this.slider.mx)?-?Number(this.slider.bx);?//x軸數據 ?let?x?=?0;?const?move?=?moveEV?=>?{ ?x?=?moveEV.x?-?downCoordinate.x;?//y?=?moveEV.y?-?downCoordinate.y; ?if?(x?>=?251?||?x?<=?0)?return?false; ?dom.style.left?=?x?+?"px";?//dom.style.top?=?y?+?"px"; ?slider.style.left?=?x?+?"px"; ?};?const?up?=?()?=>?{?document.removeEventListener("mousemove",?move);?document.removeEventListener("mouseup",?up); ?dom.style.left?=?"";?console.log(x,?checkx);?let?max?=?checkx?-?5;?let?min?=?checkx?-?10;?//允許正負誤差1 ?if?((max?>=?x?&&?x?>=?min)?||?x?===?checkx)?{?console.log("滑動解鎖成功");?this.puzzle?=?true;?this.tips?=?"驗證成功"; ?setTimeout(()?=>?{?this.visible?=?false; ?},?500); ?}?else?{?console.log("拼圖位置不正確");?this.tips?=?"驗證失敗,請重試";?this.puzzle?=?false;?this.canvasInit(); ?} ?};?document.addEventListener("mousemove",?move);?document.addEventListener("mouseup",?up); }復制代碼
4、總結
核心點比較多,寫過之后發現不難,關鍵在于去寫
個人該頁面git地址:https://github.com/ht-sauce/dream
該頁面處于項目的
路由訪問為:http://localhost:8080/consumer
5、完整的頁面代碼
<template> ?<div?id="login"> ?<el-form?class="loginFrom"?:model="logindata"?:rules="rules"?ref="ruleForm"> ?<el-form-item?class="login-item"> ?<h2?class="login-title">海天醬油登錄中心</h2> ?</el-form-item> ?<el-form-item?prop="userName"> ?<el-input ?class="login-inputorbuttom" ?prefix-icon="el-icon-user" ?placeholder="登錄名" ?v-model="logindata.userName" ?></el-input> ?</el-form-item> ?<el-form-item?prop="password"> ?<el-input ?class="login-inputorbuttom" ?prefix-icon="el-icon-lock" ?placeholder="密碼" ?v-model="logindata.password" ?></el-input> ?</el-form-item> ?<!--<el-form-item?prop="verificationCode"> ?<el-input ?class="login-inputorbuttom" ?v-model="logindata.verificationCode" ?></el-input> ?</el-form-item>--> ?<el-form-item?class="login-item"> ?<el-button ?class="login-inputorbuttom?login-bottom" ?type="primary" ?v-popover:popover ?@click="visible?=?!visible" ?>登?錄</el-button ?> ?</el-form-item> ?</el-form> ?<!--驗證碼彈窗--> ?<el-popover ?popper-class="slidingPictures" ?ref="popover" ?trigger="manual" ?v-model="visible" ?> ?<div?class="sliding-pictures"> ?<div?class="vimg"> ?<canvas?id="sliderBlock"></canvas> ?<canvas?id="codeImg"></canvas> ?</div> ?<div?class="slider"> ?<div?class="track"?:class="{?pintuTrue:?puzzle?}"> ?{{?tips?}} ?</div> ?<div?class="button?el-icon-s-grid"?@mousedown.prevent="drag"></div> ?</div> ?<div?class="operation"> ?<span ?title="關閉驗證碼" ?@click="visible?=?false" ?class="el-icon-circle-close" ?></span> ?<span ?title="刷新驗證碼" ?@click="canvasInit" ?class="el-icon-refresh-left" ?></span> ?</div> ?</div> ?</el-popover> ?</div></template><script>export?default?{?name:?"login", ?data()?{?return?{?tips:?"拖動左邊滑塊完成上方拼圖",?logindata:?{?userName:?"",?password:?"",?verificationCode:?"" ?},?rules:?{},?visible:?false,?//滑塊x軸數據 ?slider:?{?mx:?0,?bx:?0 ?},?//拼圖是否正確 ?puzzle:?false ?}; ?},?watch:?{ ?visible(e)?{?if?(e?===?true)?{?this.canvasInit();?this.puzzle?=?false; ?} ?} ?}, ?beforeCreate()?{}, ?created()?{}, ?beforeMount()?{}, ?mounted()?{},?methods:?{?//拼圖驗證碼初始化 ?canvasInit()?{?//生成指定區間的隨機數 ?const?random?=?(min,?max)?=>?{?return?Math.floor(Math.random()?*?(max?-?min?+?1)?+?min); ?};?//x:?254,?y:?109 ?let?mx?=?random(127,?244), ?bx?=?random(10,?128), ?y?=?random(10,?99);?this.slider?=?{?mx,?bx?};?this.draw(mx,?bx,?y); ?},?//鼠標按下 ?drag(e)?{?console.log("鼠標按下",?e);?let?dom?=?e.target;?//dom元素 ?let?slider?=?document.querySelector("#sliderBlock");?//滑塊dom ?const?downCoordinate?=?{?x:?e.x,?y:?e.y?};?//正確的滑塊數據 ?let?checkx?=?Number(this.slider.mx)?-?Number(this.slider.bx);?//x軸數據 ?let?x?=?0;?const?move?=?moveEV?=>?{ ?x?=?moveEV.x?-?downCoordinate.x;?//y?=?moveEV.y?-?downCoordinate.y; ?if?(x?>=?251?||?x?<=?0)?return?false; ?dom.style.left?=?x?+?"px";?//dom.style.top?=?y?+?"px"; ?slider.style.left?=?x?+?"px"; ?};?const?up?=?()?=>?{?document.removeEventListener("mousemove",?move);?document.removeEventListener("mouseup",?up); ?dom.style.left?=?"";?console.log(x,?checkx);?let?max?=?checkx?-?5;?let?min?=?checkx?-?10;?//允許正負誤差1 ?if?((max?>=?x?&&?x?>=?min)?||?x?===?checkx)?{?console.log("滑動解鎖成功");?this.puzzle?=?true;?this.tips?=?"驗證成功"; ?setTimeout(()?=>?{?this.visible?=?false; ?},?500); ?}?else?{?console.log("拼圖位置不正確");?this.tips?=?"驗證失敗,請重試";?this.puzzle?=?false;?this.canvasInit(); ?} ?};?document.addEventListener("mousemove",?move);?document.addEventListener("mouseup",?up); ?}, ?draw(mx?=?200,?bx?=?20,?y?=?50)?{?let?mainDom?=?document.querySelector("#codeImg");?let?bg?=?mainDom.getContext("2d");?let?width?=?mainDom.width;?let?height?=?mainDom.height;?let?blockDom?=?document.querySelector("#sliderBlock");?let?block?=?blockDom.getContext("2d");?//重新賦值,讓canvas進行重新繪制 ?blockDom.height?=?height; ?mainDom.height?=?height;?let?imgsrc?=?require("../assets/images/back.jpg");?let?img?=?document.createElement("img"); ?img.style.objectFit?=?"scale-down"; ?img.src?=?imgsrc; ?img.onload?=?function()?{ ?bg.drawImage(img,?0,?0,?width,?height); ?block.drawImage(img,?0,?0,?width,?height); ?};?let?mainxy?=?{?x:?mx,?y:?y,?r:?9?};?let?blockxy?=?{?x:?bx,?y:?y,?r:?9?};?this.drawBlock(bg,?mainxy,?"fill");?this.drawBlock(block,?blockxy,?"clip"); ?},?//繪制拼圖 ?drawBlock(ctx,?xy?=?{?x:?254,?y:?109,?r:?9?},?type)?{?let?x?=?xy.x, ?y?=?xy.y, ?r?=?xy.r, ?w?=?40;?let?PI?=?Math.PI;?//繪制 ?ctx.beginPath();?//left ?ctx.moveTo(x,?y);?//top ?ctx.arc(x?+?(w?+?5)?/?2,?y,?r,?-PI,?0,?true); ?ctx.lineTo(x?+?w?+?5,?y);?//right ?ctx.arc(x?+?w?+?5,?y?+?w?/?2,?r,?1.5?*?PI,?0.5?*?PI,?false); ?ctx.lineTo(x?+?w?+?5,?y?+?w);?//bottom ?ctx.arc(x?+?(w?+?5)?/?2,?y?+?w,?r,?0,?PI,?false); ?ctx.lineTo(x,?y?+?w); ?ctx.arc(x,?y?+?w?/?2,?r,?0.5?*?PI,?1.5?*?PI,?true); ?ctx.lineTo(x,?y);?//修飾,沒有會看不出效果 ?ctx.lineWidth?=?1; ?ctx.fillStyle?=?"rgba(255,?255,?255,?0.5)"; ?ctx.strokeStyle?=?"rgba(255,?255,?255,?0.5)"; ?ctx.stroke(); ?ctx[type](); ?ctx.globalCompositeOperation?=?"xor"; ?} ?} };</script><style>.slidingPictures?{?padding:?0;?width:?300px;?border-radius:?2px; }</style><style?scoped?lang="scss"></style>
最后
喜歡的可以點個關注嗎,小可樂也不容易呢
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。