短信图片滑动校验

49 阅读2分钟

操作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;
  }
}