vue开发H5 实现滑动图片获取验证码

在这里插入图片描述

<template>
  <div>
    <van-button type="primary" @click="sendVerification">获取验证码</van-button>
    <van-popup
      v-model="captchaVisible"
      closeable
      position="bottom"
      class="login-captcha"
      :close-on-click-overlay="false"
      safe-area-inset-bottom
    >
      <div class="login-captcha-title">拖动下方滑块完成验证</div>
      <div class="login-captcha-main">
        <img alt="网络错误" ref="shade" class="login-captcha-shade" />
        <img alt="网络错误" ref="puzzle" class="login-captcha-puzzle" />
        <div class="login-captcha-refresh" @click="getCaptchaPuzzle"></div>
      </div>
      <van-slider
        class="login-captcha-slider"
        active-color="transparent"
        v-model="slider"
        @change="getVerification"
      >
        <template #button>
          <div class="login-captcha-slider__button"></div>
        </template>
      </van-slider>
    </van-popup>
  </div>
</template>

<script>
import { getCaptchaPuzzle, getVerification } from '@/api/utils'

export default {
  data() {
    return {
      captchaVisible: false,
      // 滑块位置 和 滑块图
      captchaPuzzle: {
        x: 0,
        y: 0,
        shadeImageUrl: '',
        cutoutImageUrl: '',
      },
      loginForm: {
        mobile: '13333333333',
      },
      slider: 13,
      puzzleLeft: 0,
    }
  },
  watch: {
    slider(val) {
      const max = (1 - this.$refs.puzzle.width / this.$refs.shade.width) * 100
      const slope = max / 74
      this.puzzleLeft = Math.min(Math.max(0, slope * (val - 13)), max)
      this.$refs.puzzle.style.left = this.puzzleLeft + '%'
    },
    captchaVisible(val) {
      if (val) {
        this.$nextTick(() => {
          this.calcPuzzle()
        })
      }
    },
  },
  methods: {
    sendVerification() {
      this.getCaptchaPuzzle().then((val) => {
        if (val) {
          this.captchaVisible = true
        }
      })
    },
    getVerification() {
      const { mobile } = this.loginForm
      const { y } = this.captchaPuzzle
      const x = ((this.puzzleLeft / 100) * this.$refs.shade.naturalWidth).toFixed(0)
      getVerification({
        Mobile: mobile,
        X: x,
        Y: y + '',
      })
        .then(({ data: res }) => {
          if (res.code !== '0') {
            this.$toast(res.msg || '验证码发送失败,请重试')
            this.slider = 13
            this.getCaptchaPuzzle()
          } else {
            this.$toast('短信验证已发送')
            this.slider = 13
            this.captchaVisible = false
          }
        })
        .catch((err) => {
          if (err) {
            this.$toast('系统或网络异常')
            this.slider = 13
            this.getCaptchaPuzzle()
          }
        })
    },
    async getCaptchaPuzzle() {
      const { mobile } = this.loginForm
      return getCaptchaPuzzle({
        Mobile: mobile,
      })
        .then((data) => {
          const res = data.data
          if (res.code !== '0') {
            this.$toast(res.msg || '系统或网络异常')
            return false
          }
          this.captchaPuzzle = res.data
          if (this.captchaVisible) {
            this.calcPuzzle()
          } else {
            this.captchaVisible = false
          }
          return true
        })
        .catch((err) => {
          if (err) {
            this.$toast('系统或网络异常')
          }
          return false
        })
    },
    async calcPuzzle() {
      const { x, y } = this.captchaPuzzle,
        shade = this.$refs.shade,
        puzzle = this.$refs.puzzle
      await this.loadImage(shade, this.captchaPuzzle.shadeImageUrl)
      puzzle.style.top = (((y + 1) / shade.naturalHeight) * 100).toFixed(6) + '%'
      await this.loadImage(puzzle, this.captchaPuzzle.cutoutImageUrl)
      puzzle.style.left = x + 'px'
    },
    async loadImage(img, url) {
      return new Promise((resolve, reject) => {
        img.onload = () => resolve(img)
        img.onerror = reject
        img.src = url
      })
    },
  },
}
</script>

<style lang="less" scoped>
.login {
  &-captcha {
    height: 408px;
    text-align: center;
    padding: 0 24px;
    box-sizing: border-box;
    border-radius: 20px 20px 0px 0px;

    &-title {
      height: 28px;
      font-size: 20px;
      font-weight: 600;
      color: #292b2e;
      line-height: 28px;
      padding: 32px 0 20px;
    }
    &-main {
      width: 327px;
      margin: 0 auto;
      border-radius: 8px;
      position: relative;
      overflow: hidden;
    }
    &-shade {
      display: block;
      width: 100%;
    }
    &-puzzle {
      position: absolute;
      width: 50px;
    }
    &-refresh {
      position: absolute;
      top: 6px;
      right: 12px;
      width: 24px;
      height: 24px;
      background: url('../../assets/images/icon_login_refresh.png');
    }
    &-slider {
      display: inline-block;
      margin-top: 32px;
      width: 290px;
      height: 52px;
      background: rgba(188, 191, 200, 0.3);
      border-radius: 27px;
      pointer-events: none;

      &::before {
        // 向右滑动验证;
        content: '\5411\53f3\6ed1\52a8\9a8c\8bc1';
        font-size: 16px;
        line-height: 52px + 8px + 8px;
        color: #8b91a0;
      }
      .van-slider__bar {
        min-width: 13%;
        max-width: 87%;
      }
      .van-slider__button-wrapper {
        pointer-events: auto;
      }
      &__button {
        pointer-events: auto;
        width: 61px;
        height: 44px;
        background-size: cover !important;
        background: url('../../assets/images/btn_verify.png');
      }
    }
  }
}
</style>

  • 7
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值