終於實現滑動拼圖驗證碼,vue

原因:以前看嗶哩嗶哩官網登陸的時候有一個拼圖驗證碼,很好奇怎麼去實現。而後就想着本身弄一個。先給你們看個人最終效果。後面再一點點拆解代碼。css

8f7fce7292dd42968558dbc8df5ac7c1


爲何想着寫這個功能呢,主要在於拼圖驗證碼在前端這裏會比較複雜而且深刻。相比文字拼寫,12306的圖片驗證碼都沒有拼圖驗證碼對前端的要求來的複雜,和難。html

我總結下知識點:前端

一、彈窗功能git

二、彈窗基於元素定位github

三、元素拖動canvas

四、canvas繪圖vim

五、基礎邏輯dom

1、彈窗和彈窗組件ide

抱歉,這裏我偷懶了直接用了elementUI的el-popover組件,因此小夥伴不懂的直接看elementUI官網的說明。函數

我我的也研究和編寫了這塊的組件功能(基於popper.js)

2、編寫基礎結構

這塊屬於html的基礎內容,也就標題黨了

3、canvas繪製圖片

一、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);
};
複製代碼

二、canvas繪製滑塊部分

就是這個圖,這個有一些知識點,不難,可是很複雜。

a7864a1c56964775927078247a3e92bb


代碼部分:

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)

繪製難點:(很重要,否則你無法理解它怎麼繪製的)

繪製主要是須要理解這裏的繪製是根據你設置一個起始點座標,而後你繪製第二次的時候線就會鏈接到第二個點,依次鏈接最後回到原點就造成一個完整的圖形。

b96e9b6c43f34ecfa9868d1eebba350b


用的是arc參數,主要是看這個圖

fill是用於填充繪製的部分,clip是裁剪出繪製的部分,利用這個就能夠出現一個扣掉的圖片和一個裁剪出來的圖片。

完成以後就是個人那個函數了。你們能夠直接拿去用。

三、讓元素跟隨鼠標點擊以後滑動

這裏其實原理很是簡單,就是有一個注意點。

原理:

鼠標點擊以後記錄當前座標,而後隨着(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);
}複製代碼

四、總結

核心點比較多,寫過以後發現不難,關鍵在於去寫

我的該頁面git地址:https://github.com/ht-sauce/dream

該頁面處於項目的

86aee8687c3b432bb2781457657387eb


路由訪問爲:http://localhost:8080/consumer

五、完整的頁面代碼

<template>
 <div id="login">
 <el-form class="loginFrom" :model="logindata" :rules="rules" ref="ruleForm">
 <el-form-item class="login-item">
 <h1 class="login-title">海天醬油登陸中心</h1>
 </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>

最後

喜歡的能夠點個關注嗎,小可樂也不容易呢

519fce4d340c4ebca62d0a29b0e0346a

相關文章
相關標籤/搜索