最近项目中要用到图片验证码,网上一查有很多,基本都是千篇一律的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组件登场
<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页面中引用
<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>
就这样吧,完事儿~