Vue图片验证码-自定义组件高级版

2020-09-30 10:22:56 浏览数 (1)

vue 自定义验证码 组件

需要注意的是如果需要在一个页面上引用两个验证码组件,那么需要自己新建两个验证码模块组件,

也就是下面的identify.vue 中的 canvas  里面的id 需要设置为不同

不然是没有效果的,

最近项目中要用到图片验证码,网上一查有很多,基本都是千篇一律的4位纯数字验证码。首先得感谢那位一代目兄台提供的模板,由于不能满足需求,所以对其进行了改造升级。

经改造的图片验证码能满足一下情形使用:①、验证码位数;②、纯数字和纯字母的验证码;③、数字和字母混合的验证码;④、字母的大小写;⑤、数字和字母(大小写)混合下各自的位数;⑥、随机生成混合情况下各自的位数;⑦、验证码随机排序。大致就这些组合吧,基本的需求都能满足,话不多说,看~

1、先把一代目兄台的canvas参数搬一下

参数

说明

类型

默认值

identifyCode

展示的图片验证码

string

1234

fontSizeMin

字体大小,最小值

number

16

fontSizeMax

字体大小,最大值

number

40

backgroundColorMin

背景颜色色值最小值,最小为

number

180

backgroundColorMax

背景颜色色值最大值,最大为255

number

240

colorMin

字体颜色色值最小值,最小为0

number

50

colorMax

字体颜色色值最大值,最大为255

number

160

lineColorMin

干扰线颜色色值最小值,最小为0

number

40

lineColorMax

干扰线颜色色值最大值,最大为255

number

180

dotColorMin

干扰点颜色色值最小值,最小为0

number

0

dotColorMax

干扰点颜色色值最大值,最大为255

number

255

contentWidth

画布宽度

number

160

contentHeight

画布高度

number

40

2、主角儿identify.vue组件登场

代码语言:javascript复制
<template>
  <canvas id="s-canvas" :width="contentWidth" :height="contentHeight"></canvas>
</template>
<script>
export default {
  name: 'SIdentify',
  props: {
    // 图片验证码
    identifyCode: {
      type: String,
      default: '1234'
    },
    // 字体最小值
    fontSizeMin: {
      type: Number,
      default: 28
    },
    // 字体最大值
    fontSizeMax: {
      type: Number,
      default: 34
    },
    // 背景颜色色值最小值,最小为0
    backgroundColorMin: {
      type: Number,
      default: 200
    },
    // 背景颜色色值最大值,最大为255
    backgroundColorMax: {
      type: Number,
      default: 240
    },
    // 字体颜色色值最小值,最小为0
    colorMin: {
      type: Number,
      default: 0
    },
    // 字体颜色色值最大值,最大为255
    colorMax: {
      type: Number,
      default: 180
    },
    // 干扰线颜色色值最小值,最小为0
    lineColorMin: {
      type: Number,
      default: 150
    },
    // 干扰线颜色色值最大值,最大为255
    lineColorMax: {
      type: Number,
      default: 200
    },
    // 干扰点颜色色值最小值,最小为0
    dotColorMin: {
      type: Number,
      default: 100
    },
    // 干扰点颜色色值最大值,最大为255
    dotColorMax: {
      type: Number,
      default: 250
    },
    // 画布宽度
    contentWidth: {
      type: Number,
      default: 100
    },
    // 画布高度
    contentHeight: {
      type: Number,
      default: 40
    }
  },
  mounted () {
    this.drawPic()
  },
  methods: {
    /**
     * 生成一个随机数
     * @param {number} min 随机数最小值
     * @param {number} max 随机数最大值
     */
    randomNum (min, max) {
      return Math.floor(Math.random() * (max - min)   min)
    },

    /**
     * 生成一个随机的颜色
     * @param {number} min 随机数最小值
     * @param {number} max 随机数最大值
     */
    randomColor (min, max) {
      const r = this.randomNum(min, max)
      const g = this.randomNum(min, max)
      const b = this.randomNum(min, max)
      return 'rgb('   r   ','   g   ','   b   ')'
    },

    /**
     * 绘制图片验证码
     */
    drawPic () {
      let canvas = document.querySelector('#s-canvas')
      let ctx = canvas.getContext('2d')
      ctx.textBaseline = 'bottom'
      // 绘制背景
      ctx.fillStyle = this.randomColor(this.backgroundColorMin, this.backgroundColorMax)
      ctx.fillRect(0, 0, this.contentWidth, this.contentHeight)
      // 绘制干扰点
      this.drawDot(ctx)
      // 绘制验证码
      for (let i = 0; i < this.identifyCode.length; i  ) {
        this.drawText(ctx, this.identifyCode[i], i)
      }
      // 绘制干扰线
      this.drawLine(ctx)
    },

    /**
     * 绘制文本单个验证码
     * @param {object} ctx canvas上下文对象
     * @param {string} txt 单个验证码
     * @param {number} i 单个验证码序号
     */
    drawText (ctx, txt, i) {
      ctx.fillStyle = this.randomColor(this.colorMin, this.colorMax)
      ctx.font = this.randomNum(this.fontSizeMin, this.fontSizeMax)   'px SimHei'
      let x = (i   1) * (this.contentWidth / (this.identifyCode.length   1))
      let y = this.randomNum(this.fontSizeMax, this.contentHeight - 5)
      let deg = this.randomNum(-45, 45)
      // 修改坐标原点和旋转角度
      ctx.translate(x, y)
      ctx.rotate(deg * Math.PI / 180)
      ctx.fillText(txt, 0, 0)
      // 恢复坐标原点和旋转角度
      ctx.rotate(-deg * Math.PI / 180)
      ctx.translate(-x, -y)
    },

    /**
     * 绘制干扰线
     * @param {object} ctx canvas上下文对象
     */
    drawLine (ctx) {
      for (let i = 0; i < 8; i  ) {
        ctx.strokeStyle = this.randomColor(this.lineColorMin, this.lineColorMax)
        ctx.beginPath()
        ctx.moveTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
        ctx.lineTo(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight))
        ctx.stroke()
      }
    },

    /**
     * 绘制干扰点
     * @param {object} ctx canvas上下文对象
     */
    drawDot (ctx) {
      for (let i = 0; i < 60; i  ) {
        ctx.fillStyle = this.randomColor(0, 255)
        ctx.beginPath()
        ctx.arc(this.randomNum(0, this.contentWidth), this.randomNum(0, this.contentHeight), 1, 0, 2 * Math.PI)
        ctx.fill()
      }
    }
  },
  watch: {
    identifyCode () {
      this.drawPic()
    }
  }
}
</script>

3、对图片验证码组件的引用

为了方便演示,直接在App.vue页面中引用

代码语言:javascript复制
<template>
  <div id="app" @click="refreshCode">
    <s-identify :identifyCode="identifyCode"></s-identify>
  </div>
</template>

<script>
import SIdentify from './components/identify'
export default {
  name: 'App',
  data () {
    return {
      makeCode: '',
      identifyCode: ''
    }
  },
  mounted () {
    this.makeIdentifyCode({ randomTypeLen: true })
  },
  methods: {
    /**
     * 随机生成一个0~9之间的数
     */
    randomNum () {
      return Math.floor(Math.random() * 10)
    },

    /**
     * 随机生成一个字母
     * @param {boolean} isUupper 生成大写字母
     */
    randomAlphabet ({ isUupper = false } = {}) {
      // a的Unicode值为97,z的Unicode值为123
      const alphabet = String.fromCharCode(Math.floor(Math.random() * 25)   97)
      if (!isUupper) {
        return alphabet
      } else {
        return alphabet.toUpperCase()
      }
    },

    /**
     * 生成图片验证码
     * @param {number} length 图片验证码位数
     * @param {boolean} typeMix 数字和字母混合
     * @param {string} pureNumber 纯数字('number')或者字母('alphabet')
     * @param {boolean} randomTypeLen 随机生成类型个数组合
     * @param {boolean} capsLookMix 字母大小写混合
     * @param {number} numLength 混合类型下的数字个数
     * @param {number} uupperLength 大写字母的个数
     */
    makeIdentifyCode ({ length = 4, typeMix = true, pureNumber = 'alphabet', randomTypeLen = false, capsLookMix = false, numLength = 2, uupperLength = 1 } = {}) {
      this.makeCode = ''
      // 数字和字母混合
      if (typeMix) {
        // 随机生成类型个数组合
        if (randomTypeLen) {
          // 字母大小写混合
          if (capsLookMix) {
            const numLength = Math.floor(Math.random() * length)   1
            const uupperLength = numLength === length ? 0 : Math.floor(Math.random() * (length - numLength))   1
            for (let i = 0; i < numLength; i  ) {
              this.makeCode  = this.randomNum()
            }
            for (let i = 0; i < uupperLength; i  ) {
              this.makeCode  = this.randomAlphabet({ isUupper: true })
            }
            if (numLength   uupperLength < length) {
              for (let i = 0; i < length - numLength - uupperLength; i  ) {
                this.makeCode  = this.randomAlphabet()
              }
            }
          } else {
            const numLength = Math.floor(Math.random() * length)   1
            for (let i = 0; i < numLength; i  ) {
              this.makeCode  = this.randomNum()
            }
            if (numLength < length) {
              for (let i = 0; i < length - numLength; i  ) {
                this.makeCode  = this.randomAlphabet()
              }
            }
          }
        } else {
          // 字母大小写混合
          if (capsLookMix) {
            const tempNumLength = numLength < 0 ? 2 : numLength > length ? 2 : numLength
            const tempUupperLength = uupperLength < 0 ? 1 : uupperLength > length - tempNumLength ? 1 : uupperLength
            for (let i = 0; i < tempNumLength; i  ) {
              this.makeCode  = this.randomNum()
            }
            for (let i = 0; i < tempUupperLength; i  ) {
              this.makeCode  = this.randomAlphabet({ isUupper: true })
            }
            if (tempNumLength   tempUupperLength < length) {
              for (let i = 0; i < length - tempNumLength - tempUupperLength; i  ) {
                this.makeCode  = this.randomAlphabet()
              }
            }
          } else {
            const tempNumLength = numLength < 0 ? 2 : numLength > length ? 2 : numLength
            for (let i = 0; i < tempNumLength; i  ) {
              this.makeCode  = this.randomNum()
            }
            if (tempNumLength < length) {
              for (let i = 0; i < length - tempNumLength; i  ) {
                this.makeCode  = this.randomAlphabet()
              }
            }
          }
        }
      } else {
        // 纯数字('number')
        if (pureNumber === 'number') {
          for (let i = 0; i < length; i  ) {
            this.makeCode  = this.randomNum()
          }
        }
        // 纯字母('alphabet')
        if (pureNumber === 'alphabet') {
          // 字母大小写混合
          if (capsLookMix) {
            const tempUupperLength = uupperLength < 0 ? 1 : uupperLength > length ? 1 : uupperLength
            for (let i = 0; i < tempUupperLength; i  ) {
              this.makeCode  = this.randomAlphabet({ isUupper: true })
            }
            if (tempUupperLength < length) {
              for (let i = 0; i < length - tempUupperLength; i  ) {
                this.makeCode  = this.randomAlphabet()
              }
            }
          } else {
            for (let i = 0; i < length; i  ) {
              this.makeCode  = this.randomAlphabet()
            }
          }
        }
      }
      this.shuffle(this.makeCode)
    },

    /**
     * 图片验证码随机排序
     * @param {string} str 图片验证码
     */
    shuffle (str) {
      this.identifyCode = [...str].sort(() => Math.random() - 0.5).join('')
      console.log(this.identifyCode)
    },

    /**
     * 更换图片验证码
     */
    refreshCode () {
      this.makeIdentifyCode({ randomTypeLen: true })
    }
  },
  components: {
    's-identify': SIdentify
  }
}
</script>

0 人点赞