随机生成4位数验证码

<!-- 图形验证码组件2024-03-26

Props:

  width: number    组件宽度

  height: number   组件高度

Events:

  @change: (code: string) => void    当验证码改变时触发该事件

Methods:

  generateCode: () => void    生成并绘制验证码

--------------------------------------------------------------

Example:

  <template>

    <input v-model="inputValue" />

    <ImgIdentifier ref="identifierRef" @change="onChange" :width="100" :height="34" />

  </template>

  <script setup>

    /** ...省略... */

    import ImgIdentifier, {IdentifierRef} from 'xxx/ImgIdentifier.vue'

    const identifierRef = ref<IdentifierRef>()

    const inputValue = ref('')

    const code = ref('')

    // 取得验证码

    const onChange = (val: string) => code.value = val

    // 判断输入值和验证码是否一致(根据实际情况看要不要忽略大小写?)

    const isValid = () => inputValue.value?.toLowerCase() === code.value?.toLowerCase()

    // 主动调用重绘方法(比如可以在输入错误后重绘一下验证码?)

    const refreshCode = () => identifierRef.value?.generateCode()

  </script>

-->

<template>

  <div class="img-identifier" :style="`width:${width}px;height:${height}px`">

    <canvas

      ref="canvasRef"

      :width="width"

      :height="height"

      @click="generateCode"

    />

  </div>

</template>

<script setup lang="ts">

import { nextTick, ref, watch } from 'vue'

export interface IdentifierRef {

  /** 生成并绘制验证码 */

  generateCode: () => void

}

const emit = defineEmits<{

  // 每当验证码生成时emit出该验证码

  (event: 'change', code: string): void

}>()

const props = defineProps<{

  /** @param width 组件宽度 */

  width: number

  /** @param height 组件宽度 */

  height: number

}>()

const canvasRef = ref<HTMLCanvasElement>()

const pool = ref('ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890')

const code = ref('')

const generateCode = () => {

  code.value = draw()

  emit('change', code.value)

}

// 暴露出验证码生成方法,可以主动调用来重绘验证码

defineExpose({ generateCode })

// 组件初始化时或者宽高改变时重绘验证码

watch(

  [() => props.width, () => props.height],

  async () => {

    await nextTick()

    generateCode()

  },

  { immediate: true },

)

// 随机数

const randomNum = (min: number, max: number) => {

  return parseInt((Math.random() * (max - min) + min + '') as string)

}

// 随机颜色

const randomColor = (min: number, max: number) => {

  const r = randomNum(min, max)

  const g = randomNum(min, max)

  const b = randomNum(min, max)

  return `rgb(${r},${g},${b})`

}

// 绘制验证码

const draw = () => {

  const ctx = canvasRef.value?.getContext('2d') as CanvasRenderingContext2D

  const { length } = pool.value

  let result = ''

  ctx.fillStyle = randomColor(180, 230) // 填充颜色

  ctx.fillRect(0, 0, props.width, props.height) // 填充的位置

  // 生成随机的4个字

  for (let i = 0; i < 4; i++) {

    const text = pool.value[randomNum(0, length)]

    result += text

    const fontSize = randomNum(18, 40) // 随机的字体大小

    const deg = randomNum(-45, 45) // 随机的旋转角度

    ctx.font = fontSize + 'px Simhei' // 定义字体

    ctx.textBaseline = 'middle' // 定义对齐方式

    ctx.fillStyle = randomColor(80, 150) // 填充不同的颜色

    ctx.save() //  保存当前的状态

    ctx.translate((props.width / 4) * i + 10, 15) // 平移

    ctx.rotate((deg * Math.PI) / 180) // 旋转

    ctx.fillText(text, -10, 0) // 在画布上绘制填色文本

    ctx.restore() // 恢复canvas之前保存的状态,防止save后执行的操作对后续绘制有影响

  }

  // 随机5条干扰线

  for (let i = 0; i < 5; i++) {

    ctx.beginPath()

    ctx.moveTo(randomNum(0, props.width), randomNum(0, props.height))

    ctx.lineTo(randomNum(0, props.width), randomNum(0, props.height))

    ctx.strokeStyle = randomColor(180, 230)

    ctx.closePath()

    ctx.stroke()

  }

  // 随机40个干扰点

  for (let i = 0; i < 40; i++) {

    ctx.beginPath()

    ctx.arc(

      randomNum(0, props.width),

      randomNum(0, props.height),

      1,

      0,

      2 * Math.PI,

    )

    ctx.closePath()

    ctx.fillStyle = randomColor(150, 200)

    ctx.fill()

  }

  return result

}

</script>

<style scoped>

.img-identifier {

  display: inline-block;

}

.img-identifier > canvas {

  cursor: pointer;

}

</style>

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值