ImageVerification.vue 7.67 KB
<template>
  <div
    v-if="visible"
    class="slidingPictures"
  >
    <div class="sliding-pictures">
      <div class="vimg">
        <canvas id="sliderBlock" />
        <canvas id="codeImg" />
      </div>
      <div class="slider">
        <div
          id="tempBlock"
          class="button el-icon-s-grid"
          @mousedown.prevent="drag"
          @touchstart="mobileDrag"
        />
        <div
          class="track"
          :class="{ pintuTrue: puzzle }"
        >
          {{ tips }}
        </div>
      </div>
      <div class="operation">
        <span
          title="关闭验证码"
          class="el-icon-circle-close"
          @click="$emit('close')"
        />
        <span
          title="刷新验证码"
          class="el-icon-refresh-left"
          @click="$emit('refresh')"
        />
      </div>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ImageVerification',
  props: {
    visible: {
      type: Boolean,
      required: true
    },
    bgImage: {
      type: String,
      required: true
    },
    sliderImage: {
      type: String,
      required: true
    },
    yHeight: {
      type: Number,
      required: true
    }
  },
  data() {
    return {
      tips: '拖动左边滑块完成上方拼图',
      puzzle: false
    }
  },
  watch: {
    visible(e) {
      if (e === true) {
        this.$nextTick(() => {
          this.draw()
        })
        this.puzzle = false
      }
    },
    bgImage: {
      handler: function (val, oldval) {
        if (this.visible && oldval !== '' && val !== oldval) {
          this.$nextTick(() => {
            this.draw()
          })
        }
      }
    }
  },
  methods: {
    mobileDrag(e) {
      const dom = e.target // dom元素
      const slider = document.querySelector('#sliderBlock') // 滑块dom
      const tempBlock = document.querySelector('#tempBlock')
      const downCoordinate = { x: e.touches[0].clientX, y: e.touches[0].clientY }
      let x = 0
      tempBlock.startX = e.touches[0].clientX - tempBlock.offsetLeft
      slider.startX = e.touches[0].clientX - slider.offsetLeft

      const move = moveEV => {
        // 设置滑动按钮不能超出父级div
        tempBlock.style.left = moveEV.touches[0].clientX - tempBlock.startX + 'px'
        if (moveEV.touches[0].clientX - tempBlock.startX <= 0) {
          tempBlock.style.left = 0 + 'px'
        }
        if (moveEV.touches[0].clientX - tempBlock.startX >= 230) {
          tempBlock.style.left = 230 + 'px'
        }

        // 设置滑块不能超出父级div
        slider.style.left = moveEV.touches[0].clientX - slider.startX + 'px'
        if (moveEV.touches[0].clientX - slider.startX <= 0) {
          slider.style.left = 0 + 'px'
        }
        if (moveEV.touches[0].clientX - slider.startX >= 225) {
          slider.style.left = 225 + 'px'
        }

        x = moveEV.touches[0].clientX - downCoordinate.x
        if (x >= 281 || x <= 0) return false
      }

      const up = e => {
        document.removeEventListener('touchmove', move)
        document.removeEventListener('touchend', up)
        // 滑块和按钮回到起始位置
        slider.style.left = ''
        dom.style.left = ''

        //  采用点击距离和最终离开距离计算滑动距离
        const movex = e.changedTouches[0].clientX - downCoordinate.x
        this.$emit('getMoveDistance', movex)
      }
      document.addEventListener('touchmove', move)
      document.addEventListener('touchend', up)
    },

    // 鼠标按下
    drag(e) {
      const dom = e.target // dom元素
      const slider = document.querySelector('#sliderBlock') // 滑块dom
      const tempBlock = document.querySelector('#tempBlock')
      const downCoordinate = { x: e.x, y: e.y }
      let x = 0
      tempBlock.startX = e.clientX - tempBlock.offsetLeft
      slider.startX = e.clientX - slider.offsetLeft

      const move = moveEV => {
        // 设置滑动按钮不能超出父级div
        tempBlock.style.left = moveEV.clientX - tempBlock.startX + 'px'
        if (moveEV.clientX - tempBlock.startX <= 0) {
          tempBlock.style.left = 0 + 'px'
        }
        if (moveEV.clientX - tempBlock.startX >= 230) {
          tempBlock.style.left = 230 + 'px'
        }

        // 设置滑块不能超出父级div
        slider.style.left = moveEV.clientX - slider.startX + 'px'
        if (moveEV.clientX - slider.startX <= 0) {
          slider.style.left = 0 + 'px'
        }
        if (moveEV.clientX - slider.startX >= 225) {
          slider.style.left = 225 + 'px'
        }
        x = moveEV.x - downCoordinate.x
        if (x >= 281 || x <= 0) return false
      }

      const up = e => {
        document.removeEventListener('mousemove', move)
        document.removeEventListener('mouseup', up)
        // 滑块和按钮回到起始位置
        slider.style.left = ''
        dom.style.left = ''

        //  采用点击距离和最终离开距离计算滑动距离
        const movex = e.x - downCoordinate.x
        this.$emit('getMoveDistance', movex)
        // this.handleSuccess(movex)
      }
      document.addEventListener('mousemove', move)
      document.addEventListener('mouseup', up)
    },

    draw() {
      const mainDom = document.querySelector('#codeImg')
      const bg = mainDom.getContext('2d')
      const height = mainDom.height

      const blockDom = document.querySelector('#sliderBlock')
      const block = blockDom.getContext('2d')
      blockDom.height = height
      mainDom.height = height
      blockDom.width = 281
      mainDom.width = 281

      // 背景图片
      const bgImage = document.createElement('img')
      bgImage.style.objectFit = 'scale-down'
      bgImage.src = this.bgImage
      bgImage.onload = function () {
        console.log('背景图片宽度', bgImage.width)
        console.log('背景图片高度', bgImage.height)
        bg.drawImage(bgImage, 0, 0, bgImage.width, bgImage.height)
      }

      // 滑块图片
      const sliderImage = document.createElement('img')
      sliderImage.src = this.sliderImage
      sliderImage.onload = () => {
        console.log('滑块高度', this.yHeight)
        block.drawImage(sliderImage, 0, this.yHeight, sliderImage.width, sliderImage.height)
      }
    }
  }
}
</script>
<style>
.slidingPictures {
  padding: 0;
  border-radius: 2px;
  display: flex;
}
</style>
<style scoped lang="scss">
.sliding-pictures {
  position: relative;

  .vimg {
    width: 280px;
    height: 171px;

    #codeImg,
    #sliderBlock {
      width: inherit;
      height: inherit;
    }

    #sliderBlock {
      position: absolute;
      z-index: 4000;
    }
  }

  .slider {
    position: relative !important;
    width: 100%;
    height: 65px;
    border-bottom: #c7c9d0 1px solid;
    display: flex;
    align-items: center;
    justify-content: flex-start;

    .track {
      margin-left: 7px;
      width: 270px;
      height: 38px;
      background: rgba(28, 136, 188, 0.5);
      border-radius: 25px;
      font-size: 14px;
      line-height: 38px;
      padding-right: 15px;
      padding-left: 70px;
    }

    .pintuTrue {
      background: #67c23a;
      color: #ffffff;
    }

    .button {
      position: absolute;
      left: 0;
      top: 7.5px;
      width: 50px;
      height: 50px;
      line-height: 48px;
      background: #ffffff;
      box-shadow: #b9bdc8 0 0 3px;
      border-radius: 50%;
      text-align: center;
      font-size: 28px;
      color: #3e5d8b;

      &:hover {
        color: #2181bd;
      }
    }
  }

  .operation {
    width: 100%;
    height: 40px;

    > span {
      color: #9fa3ac;
      display: inline-block;
      width: 40px;
      font-size: 25px;
      line-height: 40px;
      text-align: center;

      &:hover {
        background: #e2e8f5;
      }
    }
  }
}
</style>