操作dom实现滑动图片验证 结构体:
<el-dialog class="imgValidation" title="请完成安全校验" :close-on-click-modal="false" :visible.sync="imgValidation"
@close="resetImgValidation" :close-on-press-escape="false" width="415px">
<i class="el-icon-refresh-right" @click="initImgValidation"></i>
<!-- 滑块图片 -->
<div class="check" :style="`background-image: url(${bgImgUrl});`">
<div class="check-content"></div>
<div class="check-block"></div>
</div>
<div class="drag">
<div class="drag-block"></div>
<div class="drag-tips">按住左边按钮向右拖动完成上方图像验证</div>
</div>
</el-dialog>
data(){
return{
mgValidation: false, // 打开滑块验证控件
isImgSuccess: false, // 滑块是否验证成功
imageToken: null, // 后台返回的图片token
bgImgUrl: this.$imgStaticUrl + '/doctorClient/home/swiper1.jpg' // 滑动图片背景图
}}
// methods里面代码
initImgValidation() {
const thas = this
// 随机生成一个x , Y坐标 用于设置校验块的位置 因为滑块有50px的宽度 所以x的范围为0-325 y的范围为0-200 (设置的滑块图片的宽高,直接x,y坐标见50可以防止生成的校验块与被校验块重叠,下面就不需要判断x < 70 ? 70 : x)
const x = random(0, 325) // const x = random(70, 325)
const y = random(0, 200)
// 随机函数
// @ param min 最小值
// @ param max 最大值
function random(min, max) {
min = Math.ceil(min)
max = Math.floor(max)
return Math.floor(Math.random() * (max - min) + min)
}
// 设置校验块的位置
const checkContent = document.querySelector('.check-content')
const checkBlock = document.querySelector('.check-block')
checkContent.style.cssText = `left:${x < 70 ? 70 : x}px;top:${y}px` // 防止生成的校验块与被校验块重叠
checkBlock.style.cssText = `background-position: -${x < 70 ? 70 : x}px -${y}px;top: ${y}px`
// 定义个运动状态 如果为true代表鼠标已经按下
let animating = false
// 定义存储鼠标按下的x坐标
let startX = 0
// 获取拖拽的滑块
const dragBlock = document.querySelector('.drag-block')
// 验证失败 滑块和被验证块恢复坐标
dragBlock.style.transform = 'translateX(0px)'
checkBlock.style.left = '0px'
// 绑定全局事件(document)而非局部元素
// 添加鼠标按下事件
dragBlock.addEventListener('mousedown', handleMousedown)
function handleMousedown(e) {
animating = true
startX = e.pageX
}
// 获取校验区域
const drag = document.querySelector('.drag')
// 添加鼠标弹起事件
document.addEventListener('mouseup', handleQjMouseup)
function handleQjMouseup(e) {
const trackRect = drag.getBoundingClientRect()
// 鼠标抬起时判断是不是在滑动框内,不是,重置滑块
if ((e.clientX < trackRect.left || e.clientX > trackRect.right) && animating == true) {
// 除了第一次,后续每次触发 initImgValidation 事件需要先清除绑定的事件
document.removeEventListener('mouseup', handleQjMouseup)
dragBlock.removeEventListener('mousedown', handleMousedown)
drag.removeEventListener('mousemove', handleMousemove)
drag.removeEventListener('mouseup', handleMouseup)
// 验证失败 滑块和被验证块恢复坐标
dragBlock.style.transform = 'translateX(0px)'
checkBlock.style.left = '0px'
// 再次随机生成滑块
thas.initImgValidation()
}
animating = false
}
// 存储移动的距离
let offsetX = 0
// 添加鼠标移动事件
drag.addEventListener('mousemove', handleMousemove)
function handleMousemove(event) {
// 没有按下鼠标 不执行
if (!animating) return
// 存储鼠标移动的距离
offsetX = event.pageX - startX
// 限制移动的范围 0-325
if (offsetX < 0 || offsetX > 325) return
// 修改可移动盒子的 x 轴坐标
dragBlock.style.transform = `translateX(${offsetX}px`
// 设置被验证滑块left值
checkBlock.style.left = `${offsetX}px`
}
drag.addEventListener('mouseup', handleMouseup)
async function handleMouseup() {
animating = false
// 根据移动的位置判断是否成功
console.log(offsetX >= x - 2 && offsetX <= x + 2, x + 2, x - 2, offsetX)
// 移动的距离和校验块的x坐标的差值小于2 代表成功
if (offsetX >= x - 2 && offsetX <= x + 2) {
thas.$message.success('校验成功')
thas.imgValidation = false // 打开滑块验证控件
thas.isImgSuccess = true // 滑块是否验证成功
try {
await thas._sendSmsCode() // 发送短信
} catch (error) {
thas.isImgSuccess = false
thas.isSendingSmsRequest = false
return
}
thas.$message.success('验证码已发送')
thas.isImgSuccess = false
thas.sendSmsCodeTimer && clearInterval(thas.sendSmsCodeTimer)
thas.remainSeconds = 60
thas.sendSmsCodeTimer = setInterval(() => {
if (--thas.remainSeconds <= 0) {
clearInterval(thas.sendSmsCodeTimer)
thas.sendSmsCodeTimer = null
thas.isSendingSmsRequest = false
}
}, 1000)
} else {
// 验证失败 滑块和被验证块恢复坐标
dragBlock.style.transform = 'translateX(0px)'
checkBlock.style.left = '0px'
thas.initImgValidation()
}
// 解绑全局事件(重要!避免内存泄漏)
document.removeEventListener('mouseup', handleQjMouseup)
dragBlock.removeEventListener('mousedown', handleMousedown)
drag.removeEventListener('mousemove', handleMousemove)
drag.removeEventListener('mouseup', handleMouseup)
}
},
resetImgValidation() {
this.imgValidation = false
},
css:
.imgValidation /deep/.el-dialog__header{
background-color: #5CDBD3;
}
.el-icon-refresh-right{
position: absolute;
top: 20px;
right: 60px;
font-size: 22px;
}
.check {
height: 250px;
background-repeat: no-repeat;
background-size: 100% 100%;
position: relative;
}
.check-content {
width: 50px;
height: 50px;
background: rgba(0, 0, 0, 0.5);
border: 1px solid #fff;
position: absolute;
top: 100px;
left: 280px;
}
.check-block {
width: 50px;
height: 50px;
border: 1px solid #fff;
background-image: inherit;
background-repeat: inherit;
background-size: 400px 300px;
background-position: -280px -100px;
position: absolute;
top: 100px;
left: 0px;
}
.drag {
width: 375px;
height: 50px;
background-color: #e3e3e3;
margin-top: 10px;
position: relative;
}
.drag-block {
width: 50px;
height: 50px;
background-color: yellowgreen;
z-index: 10;
position: absolute;
top: 0;
left: 0;
}
.drag-tips {
position: absolute;
top: 0;
left: 0;
right: 0;
text-align: center;
line-height: 50px;
font-size: 12px;
color: #8a8a8a;
}
}