JavaScript实现登录拼图验证的示例代码
小孔菜菜 人气:0看到一个好文,所以模仿这实现一个登录时的拼图验证。效果展示如下。
底图实现
首先实现一个盒子,存放我的
// html <div class="check"> </div>
// css .check { width: 400px; height: 300px; background-image: url(./img/bg.avif); background-size: 100% 100%; background-repeat: no-repeat; position: relative; filter: brightness(80%); }
底图中的验证区域通过伪类实现
.check::before { position: absolute; content: ''; width: 50px; height: 50px; background: rgba(0, 0, 0, 0.5); top: 100px; left: 280px; }
被验证区域块
被验证区域有点类似于抠图的概念了,通过从底图中截取一部分,并且改变放置的位置,使其看起来是整体的一部分。核心的思想就是利用了background-position
设置负值,获取图片的部分
<div class="check"> <!-- 验证区块通过伪类实现 --> <!-- 被验证区块 --> <div class="check-box" id="check-box"></div> </div>
.check-box { width: 50px; height: 50px; position: absolute; top: 100px; left: 0; background-image: inherit; background-repeat: inherit; background-size: 400px 300px; background-position: -280px -100px; border: 1px solid #fff; }
滑块区域
滑块的html+css实现特别简单,这里有一个对号是通过border-width实现的,感觉很妙呀!
<!-- 拖动条 --> <div class="drag"> <!-- 滑块 --> <div class="drag-box" id="drag-box"> <!-- 对号 --> <div id="drag-mark" class="drag-mark"></div> </div> <!-- 文字提示 --> <div class="drag-tips"> <span>按住左边按钮向右拖动完成上方图像验证</span> </div> </div>
.drag { width: 400px; height: 50px; background-color: #e3e3e3; margin-top: 10px; position: relative; } .drag-box { position: absolute; top: 0; left: 0; width: 50px; height: 50px; background-color: aquamarine; z-index: 10; display: flex; justify-content: center; align-items: center; } .drag-tips { position: absolute; top: 0; left: 0; display: flex; justify-content: end; align-items: center; width: 95%; height: 100%; font-size: 12px; color: #8a8a8a; user-select: none; } .drag-mark { width: 8px; height: 16px; border-color: #009933; border-style: solid; border-width: 0 3px 5px 0; transform: rotate(45deg); opacity: 0; transition: 0.3s; }
动态滑动
滑动的过程涉及鼠标按下、鼠标移动、鼠标弹起
鼠标按下
保存鼠标的初始水平值pageX,方便下一步计算移动值
监听鼠标的移动事件
// 鼠标按下 const mouseDown = (event) => { // 获取鼠标坐标 preX = event.pageX // 监听鼠标移动 document.addEventListener('mousemove', mouseMove) } ···
鼠标移动
- 获取当前的鼠标值pageX,和原始值做减法,得到移动值
- 移除监听鼠标的移动事件
- 设置滑块和被验证区域块的移动值
思考:这里使用pageX,clientX都可以实现相应的效果,但是使用offsetX的时候会出现闪烁的效果,看到很多帖子说使用pointer-events: none
解决,我的不可以,留一个疑问在这里吧:鼠标的移动值到底怎么计算?,等日后参悟透了再来更新。
// 鼠标移动 const mouseMove = (event) => { const { pageX } = event // 移动距离 const moveX = pageX - preX // 移动不能超出区域 if (moveX < 0 || moveX > 350) { return } check.style.transform = `translateX(${moveX}px)` drag.style.transform = `translateX(${moveX}px)` }
鼠标抬起
- 获取当前的鼠标值pageX,和原始值做减法,得到移动值
- 判断移动值是否在有效区间之内
- 在有效区间,调用验证通过的回调函数
- 不在有效区间,应该给滑块和被验证区域块设计回弹动画的效果
// 鼠标抬起 const mouseUp = (event) => { // 移出移动事件 document.removeEventListener('mousemove', mouseMove) const { pageX } = event const moveX = pageX - preX if (moveX < 278 || moveX > 285) { ....... } else { success() } }
有效验证
有效验证是指验证通过的回调函数
// 校验回调函数 const success = () => { console.log('通过校验!') }
动画处理
对于回弹动画的处理,首先是css方面,设计动画效果,然后是js方面,滑块和被验证区域赋值动画效果,监听动画结束事件,动画结束的时候,滑块和被验证区域回到0的位置,清除动画效果。
/* 回弹动画 */ @keyframes move { to { transform: translateX(0); } }
if (moveX < 278 || moveX > 285) { // 没有在校验区域 增加回弹动画 drag.style.animation = 'move 0.5s ease-in-out' check.style.animation = 'move 0.5s ease-in-out' // 动画结束回调 const animationEnd = () => { check.style.transform = `translateX(${0}px)` drag.style.transform = `translateX(${0}px)` mark.style.opacity = 0 // 清除动画 drag.style.animation = '' check.style.animation = '' // 取消监听动画结束回调 document.removeEventListener('animationEnd', animationEnd) } document.addEventListener('animationend', animationEnd) }
完整代码
图片是远程服务器上的,我下载到本地了,点击上面的底图链接可以获取到原图。
<!-- * @Author: Kongjingjing * @Date: 2023-01-10 15:37:03 * @Description: --> <!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>Document</title> <style> .check { width: 400px; height: 300px; background-image: url(./img/bg.avif); background-size: 100% 100%; background-repeat: no-repeat; position: relative; filter: brightness(80%); } .check::before { position: absolute; content: ''; width: 50px; height: 50px; background: rgba(0, 0, 0, 0.5); top: 100px; left: 280px; } .check-box { width: 50px; height: 50px; position: absolute; top: 100px; left: 0; background-image: inherit; background-repeat: inherit; background-size: 400px 300px; background-position: -280px -100px; border: 1px solid #fff; } .drag { width: 400px; height: 50px; background-color: #e3e3e3; margin-top: 10px; position: relative; } .drag-box { position: absolute; top: 0; left: 0; width: 50px; height: 50px; background-color: aquamarine; z-index: 10; display: flex; justify-content: center; align-items: center; } .drag-tips { position: absolute; top: 0; left: 0; display: flex; justify-content: end; align-items: center; width: 95%; height: 100%; font-size: 12px; color: #8a8a8a; user-select: none; } /* 回弹动画 */ @keyframes move { to { transform: translateX(0); } } .drag-mark { width: 8px; height: 16px; border-color: #009933; border-style: solid; border-width: 0 3px 5px 0; transform: rotate(45deg); opacity: 0; transition: 0.3s; } </style> </head> <body> <div class="check"> <!-- 验证区块通过伪类实现 --> <!-- 被验证区块 --> <div class="check-box" id="check-box"></div> </div> <!-- 拖动条 --> <div class="drag"> <!-- 滑块 --> <div class="drag-box" id="drag-box"> <!-- 对号 --> <div id="drag-mark" class="drag-mark"></div> </div> <!-- 文字提示 --> <div class="drag-tips"> <span>按住左边按钮向右拖动完成上方图像验证</span> </div> </div> </body> <script> // 被验证区块 const check = document.getElementById('check-box') // 滑块 const drag = document.getElementById('drag-box') // 对号 const mark = document.getElementById('drag-mark') let preX = 0 // 鼠标按下 const mouseDown = (event) => { // 获取鼠标坐标 preX = event.pageX // 监听鼠标移动 document.addEventListener('mousemove', mouseMove) } // 鼠标移动 const mouseMove = (event) => { const { pageX } = event // 移动距离 const moveX = pageX - preX // 移动不能超出区域 if (moveX < 0 || moveX > 350) { return } check.style.transform = `translateX(${moveX}px)` drag.style.transform = `translateX(${moveX}px)` } // 鼠标抬起 const mouseUp = (event) => { // 移出移动事件 document.removeEventListener('mousemove', mouseMove) const { pageX } = event const moveX = pageX - preX if (moveX < 278 || moveX > 285) { // 没有在校验区域 增加回弹动画 drag.style.animation = 'move 0.5s ease-in-out' check.style.animation = 'move 0.5s ease-in-out' // 动画结束回调 const animationEnd = () => { check.style.transform = `translateX(${0}px)` drag.style.transform = `translateX(${0}px)` mark.style.opacity = 0 // 清除动画 drag.style.animation = '' check.style.animation = '' // 取消监听动画结束回调 document.removeEventListener('animationEnd', animationEnd) } document.addEventListener('animationend', animationEnd) } else { // 对号 mark.style.opacity = 1 success() } } // 校验回调函数 const success = () => { console.log('通过校验!') } // 滑块绑定鼠标按下事件 drag.addEventListener('mousedown', mouseDown) document.addEventListener('mouseup', mouseUp) </script> </html>
加载全部内容