前言
由于开发中,产品有需求,要求开发一个可以自定义字符的随机获取4个字符的验证码组件,然后我就仿照流行的验证码功能写了一个
运行效果
HTML
<template>
<!-- 验证码画布容器,点击后会触发验证码刷新 -->
<span class="s-canvas" @click="changeCode">
<canvas
id="s-canvas"
:width="contentWidth"
:height="contentHeight"
></canvas>
</span>
</template>
JS
<script setup lang="ts">
import { ref, watch, onMounted, defineProps, defineEmits } from "vue";
// 定义组件的 props
const props = defineProps({
// 默认的验证码字符集,包含数字和字母
identifyCodes: {
type: String,
default: "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ",
},
// 字体最小值,用于生成随机字体大小
fontSizeMin: {
type: Number,
default: 25,
},
// 字体最大值,用于生成随机字体大小
fontSizeMax: {
type: Number,
default: 35,
},
// 背景色最小值,用于生成随机背景颜色
backgroundColorMin: {
type: Number,
default: 200,
},
// 背景色最大值,用于生成随机背景颜色
backgroundColorMax: {
type: Number,
default: 220,
},
// 干扰点的颜色最小值
dotColorMin: {
type: Number,
default: 60,
},
// 干扰点的颜色最大值
dotColorMax: {
type: Number,
default: 120,
},
// 验证码容器的宽度
contentWidth: {
type: Number,
default: 100,
},
// 验证码容器的高度
contentHeight: {
type: Number,
default: 44,
},
});
// 定义 emits,用于向父组件发送验证码变化事件
const emit = defineEmits<{
(event: "update:changeCode", identifyCode: string): void;
}>();
// 创建响应式的验证码内容
const identifyCode = ref("");
// 监听 identifyCode 的变化,重新绘制验证码
watch(identifyCode, () => {
// 每次验证码变化时,重新绘制验证码
drawPic();
});
// 在组件挂载时,生成初始验证码
onMounted(() => {
// 生成4位随机验证码
makeCode(props.identifyCodes, 4);
drawPic();
});
// 生成一个指定范围内的随机整数
const randomNum = (min: number, max: number): number => {
return Math.floor(Math.random() * (max - min) + min);
};
// 生成一个随机颜色,范围由 min 到 max
const randomColor = (min: number, max: number): string => {
const r = randomNum(min, max); // 红色值
const g = randomNum(min, max); // 绿色值
const b = randomNum(min, max); // 蓝色值
return `rgb(${r},${g},${b})`; // 返回 RGB 颜色字符串
};
// 绘制验证码的图片
const drawPic = () => {
const canvas = document.getElementById("s-canvas") as HTMLCanvasElement;
// 获取画布上下文,进行绘制
const ctx = canvas.getContext("2d")!;
// 设置文本的基线位置
ctx.textBaseline = "bottom";
// 绘制随机背景色
ctx.fillStyle = randomColor(
props.backgroundColorMin,
props.backgroundColorMax
);
// 绘制背景矩形
ctx.fillRect(0, 0, props.contentWidth, props.contentHeight);
// 绘制验证码的文字
for (let i = 0; i < identifyCode.value.length; i++) {
drawText(ctx, identifyCode.value[i], i);
}
// 绘制干扰线
drawLine(ctx);
// 绘制干扰点
drawDot(ctx);
};
// 绘制验证码文字
const drawText = (ctx: CanvasRenderingContext2D, txt: string, i: number) => {
// 随机生成文字颜色
ctx.fillStyle = randomColor(50, 160);
// 随机生成字体大小
ctx.font = `${randomNum(props.fontSizeMin, props.fontSizeMax)}px SimHei`;
// 计算每个字符的 x 坐标
const x = (i + 1) * (props.contentWidth / (identifyCode.value.length + 1));
// 计算字符的 y 坐标
const y = randomNum(props.fontSizeMax, props.contentHeight - 5);
// 随机生成字符旋转角度
const deg = randomNum(-30, 30);
// 修改坐标原点并旋转
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);
};
// 绘制干扰线
const drawLine = (ctx: CanvasRenderingContext2D) => {
for (let i = 0; i < 4; i++) {
// 绘制 4 条干扰线
ctx.strokeStyle = randomColor(100, 200);
ctx.beginPath();
ctx.moveTo(
randomNum(0, props.contentWidth),
randomNum(0, props.contentHeight)
); // 起始点
ctx.lineTo(
randomNum(0, props.contentWidth),
randomNum(0, props.contentHeight)
); // 终止点
ctx.stroke(); // 绘制路径
}
};
// 绘制干扰点
const drawDot = (ctx: CanvasRenderingContext2D) => {
for (let i = 0; i < 30; i++) {
// 绘制 30 个干扰点
// 随机生成点的颜色
ctx.fillStyle = randomColor(0, 255);
ctx.beginPath();
ctx.arc(
// 圆心 x 坐标
randomNum(0, props.contentWidth),
// 圆心 y 坐标
randomNum(0, props.contentHeight),
// 半径
1,
// 起始角度
0,
// 结束角度
2 * Math.PI
);
// 填充圆点
ctx.fill();
}
};
// 切换验证码
const changeCode = () => {
// 清空当前验证码
identifyCode.value = "";
// 重新生成验证码
makeCode(props.identifyCodes, 4);
};
// 生成验证码
const makeCode = (e: string, n: number) => {
// 清空当前验证码内容
identifyCode.value = "";
for (let i = 0; i < n; i++) {
// 生成 n 位验证码 从字符集中随机选取字符
identifyCode.value += e[randomNum(0, e.length)];
}
// 向父组件发送新的验证码
emit("update:changeCode", identifyCode.value);
};
</script>
组件使用
<number-code
:identifyCodes="'1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ'"
:fontSizeMin=30
:fontSizeMax=50
:contentWidth=150
:contentHeight=50
/>
获取验证码API
changeCode | 监听验证码变化事件 | val |
传参参数
identifyCodes | 自定义验证码字符集(随机取4个) | string | - |
fontSizeMin | 字体最小值(px) | number | 25 |
fontSizeMax | 字体最大值(px) | number | 35 |
contentWidth | 容器宽度(px) | number | 100 |
contentHeight | 容器高度(px) | number | 45 |
backgroundColorMin | 背景色最小值(RGB值范围) | number | 200 |
backgroundColorMax | 背景色最大值(RGB值范围) | number | 220 |
dotColorMin | 干扰点颜色最小值(RGB值范围) | number | 60 |
dotColorMax | 干扰点颜色最大值(RGB值范围) | number | 120 |