js前端實(shí)現(xiàn)登錄拼圖驗(yàn)證功能
前言
不知各位朋友現(xiàn)在在 web 端進(jìn)行登錄的時(shí)候有沒有注意一個(gè)變化,以前登錄的時(shí)候是直接賬號(hào)密碼通過就可以直接登錄,再后來圖形驗(yàn)證碼,數(shù)字結(jié)果運(yùn)算驗(yàn)證,到現(xiàn)在的拼圖驗(yàn)證。這一系列的轉(zhuǎn)變都是為了防止機(jī)器操作,但對(duì)于我們來說,有億點(diǎn)麻煩,但也沒辦法呀。
今天我們也一起實(shí)現(xiàn)一個(gè)拼圖驗(yàn)證。
核心功能
- 滑動(dòng)解鎖功能
- 重置位置功能
- 滑塊進(jìn)度條功能
- 結(jié)果提示功能
- 隨機(jī)生成滑塊位置
實(shí)現(xiàn)原理
這個(gè)的實(shí)現(xiàn)原理并不復(fù)雜,我們只需要一張背景圖作為我們的拼接素材,再單獨(dú)定義一個(gè)盒子并拖拽移動(dòng)它到指定位置就可以完成拼圖驗(yàn)證功能了
實(shí)現(xiàn)前端登錄拼圖驗(yàn)證
搭建框架
我們要實(shí)現(xiàn)這個(gè)功能,我們需要先搭建出來一個(gè)樣式結(jié)構(gòu)出來。
// css :root { --iWidth: 50px; --iHeight: 50px; } .wrapper { width: 100%; width: 500px; margin: 0 auto; } .container { position: relative; width: 500px; height: 300px; box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.5); border-radius: 8px; background-image: url("./bg.webp"); background-size: 100% 100%; background-repeat: no-repeat; } // html <div class="wrapper"> <!-- 容器 --> <div class="container"></div> </div>
我們根據(jù)剛才的樣式結(jié)構(gòu)搭建完成后,它就長下圖這個(gè)樣子
添加被校驗(yàn)區(qū)域及校驗(yàn)區(qū)域
我們需要添加一個(gè)被校驗(yàn)區(qū)域及校驗(yàn)區(qū)域,用來做我們的滑動(dòng)校驗(yàn)
??!在滑動(dòng)區(qū)域中使用 background-position 配合 background-size 根據(jù)校驗(yàn)區(qū)域的大小和位置切出滑動(dòng)區(qū)域的圖
// css // 被校驗(yàn)區(qū)域樣式 .container-move { position: absolute; width: var(--iWidth); height: var(--iHeight); top: 180px; left: 0; z-index: 3; background-size: 500px 300px; background-repeat: no-repeat; background-image: url("./img/bg.jpg"); background-position: -180px -200px; } // 校驗(yàn)區(qū)域樣式 .container-empty { position: absolute; width: var(--iWidth); height: var(--iHeight); top: 180px; left: 200px; background: #fff; z-index: 2; opacity: .6; } // html <div class="wrapper"> <!-- 容器 --> <div class="container"> <!-- 被校驗(yàn)區(qū)域 --> <div class="container-move"></div> <!-- 校驗(yàn)區(qū)域 --> <div class="container-empty"></div> </div> </div>
效果圖
添加滑塊、滑塊背景、拖動(dòng)條、提示文字
拖動(dòng)條來控制滑塊移動(dòng)的最大距離
滑塊來控制被校驗(yàn)區(qū)域的移動(dòng)距離
提示文字是提示用戶這里可以操作解鎖
拖動(dòng)條內(nèi)的滑塊、滑塊背景、提示文字都對(duì)拖動(dòng)條進(jìn)行絕對(duì)定位
主要是為了控制他們的層級(jí),提示文字 < 滑塊背景 < 滑塊
我們添加滑塊、滑塊背景、拖動(dòng)條、提示文字
// css /* 拖動(dòng)條樣式 */ .slider-control { width: 500px; height: 50px; margin-top: 20px; border-radius: 4px; position: relative; overflow: hidden; background: #f2f2f2; box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.5); } /* 滑塊 */ .slider { width: var(--iWidth); height: var(--iHeight); position: absolute; left: 0; top: 0; background: skyblue; border-radius: 4px; text-align: center; line-height: var(--iHeight); transition: all; user-select: none; z-index: 3; } /* 滑塊背景 */ .slider-shadow { position: absolute; left: 0; top: 0; width: 0; height: 50px; background: #fff; z-index: 2; } /* 提示文案 */ .slider-info { text-align: center; color: rgba(102, 102, 102, 1); margin: 0; line-height: 50px; } // html <div class="wrapper"> <!-- 控制容器 --> <div class="slider-control"> // 提示文字 <p class="slider-info">按住左邊按鈕向右拖動(dòng)完成上方圖像驗(yàn)證</p> // 滑塊 <div class="slider">>></div> // 滑塊背景 <div class="slider-shadow"></div> </div> </div>
效果圖如下
添加交互
到這里拼圖驗(yàn)證的樣式結(jié)構(gòu)已經(jīng)完成了,接下來我們需要對(duì)相關(guān)的元素添加交互事件
讓滑塊可以在拖動(dòng)條內(nèi)隨意拖拽
// 我太懶了,不想寫那么一大堆,這里封裝個(gè)用 class 來獲取實(shí)例的函數(shù) function getElement(elName) { return document.getElementsByClassName(elName)[0] } // 獲取實(shí)例 let sliderBox = getElement('slider'); // 滑動(dòng)的塊 let sliderShadow = getElement('slider-shadow'); // 滑動(dòng)背景 let container = getElement("container"); // 容器的實(shí)例 let sliderMove = getElement("container-move"); // 解鎖的塊 let containerWidth = container.clientWidth; // 獲取容器的可視區(qū)寬度 let sliderMoveWidth = sliderMove.clientWidth; // 被校驗(yàn)區(qū)域的可視區(qū)寬度 let maxDistance = (containerWidth - sliderMoveWidth); // 根據(jù)容器和被校驗(yàn)區(qū)域的大小限制滑塊移動(dòng)最大的距離 /** * 監(jiān)聽鼠標(biāo)在sliderBox(滑動(dòng)塊)按下時(shí) mousedowm, 并根據(jù)可視區(qū)左邊的距離減去鼠標(biāo)按下的距離, 獲取到實(shí)際 * document的onmouseMove和mouseup事件, * 獲取鼠標(biāo)滑動(dòng)的位置 */ sliderBox.onmousedown = function (moveStart) { // console.log("?? ~ file: index.html ~ line 94 ~ moveStart", moveStart) let left = moveStart.clientX - sliderBox.offsetLeft; // 獲取按下時(shí)元素距離可視區(qū)左邊的位置 - 鼠標(biāo)按下時(shí)的位置, 獲取到鼠標(biāo)距離元素邊緣的位置 let lefta // 記錄移動(dòng)的距離 // 鼠標(biāo)按下持續(xù)移動(dòng)中 document.onmousemove = function (moveTo) { // 如果不減去 left 那么就會(huì)導(dǎo)致鼠標(biāo)一直在移動(dòng)元素的左邊框上, 也就會(huì)出現(xiàn)元素不跟著鼠標(biāo)走的問題, 有偏差 lefta = moveTo.clientX - left; // 元素移動(dòng)的距離 // 限制元素的移動(dòng)距離,不能小于0, 或者大于最大寬度 - 元素本身的距離, 否則就初始化位置 if (lefta < 0) { lefta = 0; } else if (maxDistance < lefta) { lefta = maxDistance; } sliderBox.style.left = lefta + 'px'; // 因?yàn)榛瑝K加了圓角的功能,所以要在這里加4px,讓滑塊剛好壓住滑塊背景,不加的話,放大的時(shí)候會(huì)比較突兀 sliderShadow.style.width = (lefta + 4) + 'px'; } // 鼠標(biāo)移動(dòng)結(jié)束 document.onmouseup = function (moveEnd) { // 解除document身上綁定的事件, 不讓事件一直觸發(fā) document.onmousemove = null; document.onmouseup = null; // 重置位置 sliderBox.style.left = 0 + 'px' sliderShadow.style.width = 0 + 'px' } }
通過我們上述代碼實(shí)現(xiàn)的效果圖,可以看到滑塊可以在拖動(dòng)條內(nèi)隨意拖動(dòng)了,且滑塊、滑塊背景、文字的層級(jí)關(guān)系也處理好了
聯(lián)動(dòng)被校驗(yàn)區(qū)域
我們?cè)谏鲜鰞?nèi)容中已經(jīng)實(shí)現(xiàn)了滑塊的自由拖拽,接下來就要聯(lián)動(dòng)被校驗(yàn)區(qū)域的滑動(dòng)位置進(jìn)行校驗(yàn)
// 獲取實(shí)例以及定義常量 let faultSize = 10; // 滑動(dòng)距離的容錯(cuò)處理, 滑動(dòng)距離- 10, +10 都可解鎖 // 事件處理 sliderBox.onmousedown = function (moveStart) { let left = moveStart.clientX - sliderBox.offsetLeft; // 獲取按下時(shí)元素距離可視區(qū)左邊的位置 - 鼠標(biāo)按下時(shí)的位置, 獲取到鼠標(biāo)距離元素邊緣的位置 let lefta // 記錄移動(dòng)的距離 // 鼠標(biāo)按下持續(xù)移動(dòng)中 document.onmousemove = function (moveTo) { // 如果不減去 left 那么就會(huì)導(dǎo)致鼠標(biāo)一直在移動(dòng)元素的左邊框上, 也就會(huì)出現(xiàn)元素不跟著鼠標(biāo)走的問題, 有偏差 lefta = moveTo.clientX - left; // 元素移動(dòng)的距離 sliderMove.style.left = lefta + 'px' } // 鼠標(biāo)移動(dòng)結(jié)束 document.onmouseup = function (moveEnd) { // 判斷滑動(dòng)距離是否相同,這里的滑動(dòng)距離允許10px的容錯(cuò),所以在當(dāng)被校驗(yàn)區(qū)域大于小于校驗(yàn)區(qū)域10px都可以校驗(yàn)通過 if (lefta >= movePointer - faultSize && lefta <= movePointer + faultSize) { success(true) } else { success(false) } init() // 重置位置 sliderBox.style.left = 0 + 'px' sliderMove.style.left = 0 + 'px' sliderShadow.style.width = 0 + 'px'; infomation.innerText = '' } } function success(valid) { let text = valid ? '成功' : "失敗" alert(`解鎖${text}`) }
通過我們的上述代碼就已經(jīng)實(shí)現(xiàn)了拼圖驗(yàn)證的功能,我們有一個(gè)隨機(jī)生成校驗(yàn)位置的功能沒有實(shí)現(xiàn),下面我們一起來實(shí)現(xiàn)一下吧
隨機(jī)生成校驗(yàn)位置
在我們登錄平臺(tái)系統(tǒng)的時(shí)候,當(dāng)我們的拼圖沒有移動(dòng)到指定位置或移動(dòng)錯(cuò)誤的時(shí)候,都是需要重新生成校驗(yàn)位置的,所以拼圖刷新功能還是很有必要的,下面我們就一起來實(shí)現(xiàn)一下吧。
// css /* 重置 */ .container-reset { position: absolute; top: 0; right: 10px; cursor: pointer; user-select: none; z-index: 1; color:#fff; } // html <div class="wrapper"> <!-- 解鎖容器 --> <div class="container"> <div class="container-reset">刷新</div> </div> </div> // js // 獲取實(shí)例 let containerReset = getElement('container-reset'); // 刷新按鈕實(shí)例 // 初始化位置 init() // 刷新拼圖位置 containerReset.onclick = function () { init() } /** * 隨機(jī)生成空白的位置, 并指定解鎖塊的高度 * */ function init() { const moveTop = Math.floor(Math.random() * 90); // 校驗(yàn)區(qū)域隨機(jī)生成的 top 高度 const moveLeft = Math.floor(Math.random() * (270 - 60) + 60); // 校驗(yàn)區(qū)域隨機(jī)生成的 left 距離 movePointer = moveLeft // 重置校驗(yàn)區(qū)域解鎖的位置 sliderMove.style.top = moveTop + 'px'; // 初始化被校驗(yàn)區(qū)域位置 containerEmpty.style.top = moveTop + 'px'; // 初始化校驗(yàn)區(qū)域 containerEmpty.style.left = moveLeft + 'px'; // 初始化校驗(yàn)區(qū)域 sliderShadow.style.width = 0 + 'px'; // 重置拖拖動(dòng)條陰影位置 sliderMove.style.backgroundPosition = `-${moveLeft}px -${moveTop}px`; // 裁剪被校驗(yàn)區(qū)域上的切圖 }
到這里看一下我們都一起實(shí)現(xiàn)文章開頭說的這些功能,是不是也沒有想象中的那么難?
- 滑動(dòng)解鎖功能
- 重置位置功能
- 滑塊進(jìn)度條功能
- 結(jié)果提示功能
- 隨機(jī)生成滑塊位置
完整代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>滑動(dòng)驗(yàn)證碼</title> <style> :root { --iWidth: 50px; --iHeight: 50px; } .wrapper { width: 100%; width: 500px; margin: 0 auto; } /* 容器 */ .container { position: relative; width: 500px; height: 300px; box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.5); border-radius: 8px; background-image: url("./img/bg.jpg"); background-size: 100% 100%; background-repeat: no-repeat; } /* 被校驗(yàn)區(qū)域 */ .container-move { position: absolute; width: var(--iWidth); height: var(--iHeight); top: 180px; left: 0; z-index: 3; background-size: 500px 300px; background-repeat: no-repeat; background-image: url("./img/bg.jpg"); background-position: -180px -200px; } /* 校驗(yàn)區(qū)域 */ .container-empty { position: absolute; width: var(--iWidth); height: var(--iHeight); top: 180px; left: 200px; background: #fff; z-index: 2; opacity: .6; } /* 重置 */ .container-reset { position: absolute; top: 0; right: 10px; cursor: pointer; user-select: none; z-index: 1; color:#fff; } /* 拖動(dòng)條樣式 */ .slider-control { width: 500px; height: 50px; margin-top: 20px; border-radius: 4px; position: relative; overflow: hidden; background: #f2f2f2; box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.5); } /* 滑塊 */ .slider { width: var(--iWidth); height: var(--iHeight); position: absolute; left: 0; top: 0; background: skyblue; border-radius: 4px; text-align: center; line-height: var(--iHeight); transition: all; user-select: none; z-index: 3; } /* 滑塊背景 */ .slider-shadow { position: absolute; left: 0; top: 0; width: 0; height: 50px; background: #fff; z-index: 2; } /* 提示文案 */ .slider-info { text-align: center; color: rgba(102, 102, 102, 1); margin: 0; line-height: 50px; user-select: none; } </style> </head> <body> <div class="wrapper"> <!-- 解鎖容器 --> <div class="container"> <div class="container-move"></div> <div class="container-empty"></div> <div class="container-reset">刷新</div> </div> <!-- 控制容器 --> <div class="slider-control"> <p class="slider-info">按住左邊按鈕向右拖動(dòng)完成上方圖像驗(yàn)證</p> <div class="slider">>></div> <div class="slider-shadow"></div> </div> <p class="infomation"></p> </div> <script> let sliderBox = getElement('slider'); // 滑動(dòng)的塊 let sliderShadow = getElement('slider-shadow'); // 滑動(dòng)的陰影 let container = getElement("container"); // 最外層的盒子 let sliderMove = getElement("container-move"); // 解鎖的塊 let containerEmpty = getElement("container-empty"); // 解鎖的位置 let infomation = getElement('infomation'); // 解鎖提示 let containerReset = getElement('container-reset'); // 重置功能 let containerWidth = container.clientWidth; // 獲取背景圖的大小 let sliderMoveWidth = sliderMove.clientWidth; // 解鎖的塊的大小 let movePointer = containerEmpty.offsetLeft // 解鎖的位置 let maxDistance = (containerWidth - sliderMoveWidth); // 根據(jù)背景和解鎖塊的寬度限制滑塊移動(dòng)最大的距離 let faultSize = 10; // 滑動(dòng)距離的容錯(cuò)處理, 滑動(dòng)距離- 10, +10 都可解鎖 init() containerReset.onclick = function () { init() } /** * 監(jiān)聽鼠標(biāo)在sliderBox(滑動(dòng)塊)按下時(shí) mousedowm, 并根據(jù)可視區(qū)左邊的距離減去鼠標(biāo)按下的距離, 獲取到實(shí)際 * document的onmouseMove和mouseup事件, * 獲取鼠標(biāo)滑動(dòng)的位置 */ sliderBox.onmousedown = function (moveStart) { let left = moveStart.clientX - sliderBox.offsetLeft; // 獲取按下時(shí)元素距離可視區(qū)左邊的位置 - 鼠標(biāo)按下時(shí)的位置, 獲取到鼠標(biāo)距離元素邊緣的位置 let lefta // 記錄移動(dòng)的距離 // 鼠標(biāo)按下持續(xù)移動(dòng)中 document.onmousemove = function (moveTo) { // 如果不減去 left 那么就會(huì)導(dǎo)致鼠標(biāo)一直在移動(dòng)元素的左邊框上, 也就會(huì)出現(xiàn)元素不跟著鼠標(biāo)走的問題, 有偏差 lefta = moveTo.clientX - left; // 元素移動(dòng)的距離 // 限制元素的移動(dòng)距離,不能小于0, 或者大于最大寬度 - 元素本身的距離, 否則就初始化位置 if (lefta < 0) { lefta = 0; } else if (maxDistance < lefta) { lefta = maxDistance; } sliderBox.style.left = lefta + 'px' sliderMove.style.left = lefta + 'px' // 因?yàn)榛瑝K加了圓角的功能,所以要在這里加4px,讓滑塊剛好壓住滑塊背景,不加的話,放大的時(shí)候會(huì)比較突兀 sliderShadow.style.width = (lefta + 4) + 'px'; } // 鼠標(biāo)移動(dòng)結(jié)束 document.onmouseup = function (moveEnd) { // 解除document身上綁定的事件, 不讓事件一直觸發(fā) document.onmousemove = null; document.onmouseup = null; // 判斷滑動(dòng)距離是否相同,這里的滑動(dòng)距離允許10px的容錯(cuò),所以在當(dāng)被校驗(yàn)區(qū)域大于小于校驗(yàn)區(qū)域10px都可以校驗(yàn)通過 if (lefta >= movePointer - faultSize && lefta <= movePointer + faultSize) { success(true) } else { success(false) } init() // 重置位置 sliderBox.style.left = 0 + 'px' sliderMove.style.left = 0 + 'px' sliderShadow.style.width = 0 + 'px'; infomation.innerText = '' } } function success(valid) { let text = valid ? '成功' : "失敗" infomation.innerText = `解鎖${text}` alert(`解鎖${text}`) } /** * 隨機(jī)生成空白的位置, 并指定解鎖塊的高度 * */ function init() { const moveTop = Math.floor(Math.random() * 90); // 滑塊隨機(jī)生成的高度 const moveLeft = Math.floor(Math.random() * (270 - 60) + 60); // 滑塊隨機(jī)生成的left距離 movePointer = moveLeft // 重置滑塊解鎖的位置 sliderMove.style.top = moveTop + 'px'; // 初始化滑塊位置 containerEmpty.style.top = moveTop + 'px'; // 初始化解鎖區(qū)域 containerEmpty.style.left = moveLeft + 'px'; sliderShadow.style.width = 0 + 'px'; sliderMove.style.backgroundPosition = `-${moveLeft}px -${moveTop}px` } function getElement(elName) { return document.getElementsByClassName(elName)[0] } </script> </body> </html>
本篇前端實(shí)現(xiàn)登錄拼圖驗(yàn)證就到此結(jié)束了,這個(gè)功能一般都是在登錄的時(shí)候用的。文章中的案例可以正常使用哦 ~
總結(jié)
到此這篇關(guān)于js前端實(shí)現(xiàn)登錄拼圖驗(yàn)證功能的文章就介紹到這了,更多相關(guān)前端登錄拼圖驗(yàn)證內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javascript設(shè)計(jì)模式之模塊模式學(xué)習(xí)筆記
這篇文章主要為大家詳細(xì)介紹了javascript設(shè)計(jì)模式之模塊模式學(xué)習(xí)筆記,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-02-02Openlayers測(cè)量距離與面積的實(shí)現(xiàn)方法
這篇文章主要為大家詳細(xì)介紹了Openlayers測(cè)量距離與面積的實(shí)現(xiàn)方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09JavaScript中執(zhí)行上下文和執(zhí)行棧
這篇文章主要介紹了JavaScript中執(zhí)行上下文和執(zhí)行棧,執(zhí)行上下文是評(píng)估和執(zhí)行JavaScript代碼的環(huán)境的抽象概念,更多相關(guān)介紹,感興趣的朋友可以參考一下2022-09-09純JS實(shí)現(xiàn)可拖拽表單的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)硪黄僇S實(shí)現(xiàn)可拖拽表單的簡(jiǎn)單實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-09-09three.js中點(diǎn)對(duì)象(Point)和點(diǎn)材質(zhì)(PointsMaterial)的具體使用
本文主要介紹了three.js中點(diǎn)對(duì)象(Point)和點(diǎn)材質(zhì)(PointsMaterial)的具體使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07JS中confirm,alert,prompt函數(shù)使用區(qū)別分析
JS中confirm,alert,prompt函數(shù)使用區(qū)別分析,需要的朋友可以參考下。2010-04-04JS密碼生成與強(qiáng)度檢測(cè)完整實(shí)例(附demo源碼下載)
這篇文章主要介紹了JS密碼生成與強(qiáng)度檢測(cè)完整實(shí)例,涉及JavaScript密碼的生成,破解時(shí)間計(jì)算,密碼安全監(jiān)測(cè)及大小寫鎖定判斷等功能的實(shí)現(xiàn)技巧,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2016-04-04JS實(shí)現(xiàn)當(dāng)前頁居中分頁效果的方法
這篇文章主要介紹了JS實(shí)現(xiàn)當(dāng)前頁居中分頁效果的方法,涉及javascript操作頁面元素與樣式的相關(guān)技巧,需要的朋友可以參考下2015-06-06