先上效果:
这里是前端生成的验证码,如果需要请求后端把随机数改为接口就好了。
使用的是vantUI的弹窗,当然核心还是canvas。我将它封装成组件了直接上代码:
<template><div class="component"><van-dialogref="dialog"v-model="showDialog"title="请输入验证码":before-close="onBeforeClose"><canvas ref="canvas" :width="width" :height="height" /><van-fieldstyle="margin-top: 20px"v-model="decode"label="验证码:"placeholder="请输入验证码"/></van-dialog></div>
</template><script>
import { Notify } from "vant";
export default {props: {width: {type: Number,default: 200,},height: {type: Number,default: 50,},length: {// 生成长度type: Number,default: 4,},fontSize: {type: Number,default: 30,},},data() {return {showDialog: false,decode: "",encode: "",};},watch: {showDialog(newVal, oldVal) {if (newVal) {this.$nextTick(() => {this.drawCaptcha();});}},},mounted() {},methods: {async onBeforeClose(action, done) {if (action === "confirm") {// 点击确定走这里if (this.encode == this.decode.toUpperCase()) {done(); // 关闭弹窗this.decode = "";} else {Notify("验证码错误!");return done(false); // 阻止弹窗关闭}} else {done(); // 关闭弹窗}},drawCaptcha() {const canvas = this.$refs.canvas;const context = canvas.getContext("2d");// 清除画布context.clearRect(0, 0, this.width, this.height);// 计算字符之间的间距const spacing = this.width / (this.length + 1);// 随机生成指定长度的大写字母验证码const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";// 验证码this.encode = "";for (let i = 0; i < this.length; i++) {const randomLetter =alphabet[Math.floor(Math.random() * alphabet.length)];this.encode += randomLetter;// 设置字体和样式context.font = `bold ${this.fontSize}px Arial`;context.fillStyle = "#000";// 随机旋转角度const angle = Math.random() * 1 - 0.2;// 平移画布以便绘制倾斜的字符,并应用间距context.translate((i + 1) * spacing, this.height / 2);context.rotate(angle);// 在画布上绘制验证码context.fillText(randomLetter, -12, 12);// 恢复画布状态,以便绘制下一个字符context.rotate(-angle);context.translate(-((i + 1) * spacing), -(this.height / 2));}// 添加干扰元素for (let i = 0; i < (this.width * this.height) / 100; i++) {const x1 = Math.random() * this.width;const y1 = Math.random() * this.height;const lengthFactor = Math.random() * 20; // 控制线的长度范围const x2 = x1 + Math.cos(Math.random() * Math.PI * 2) * lengthFactor;const y2 = y1 + Math.sin(Math.random() * Math.PI * 2) * lengthFactor;// 生成线context.strokeStyle = this.getRandomColor();context.lineWidth = 1;context.beginPath();context.moveTo(x1, y1);context.lineTo(x2, y2);context.stroke();// 生成点const x = Math.random() * this.width;const y = Math.random() * this.height;context.fillStyle = this.getRandomColor();context.fillRect(x, y, 1, 1);}},getRandomColor() {const letters = "0123456789ABCDEF";let color = "#";for (let i = 0; i < 6; i++) {color += letters[Math.floor(Math.random() * 16)];}return color;},},
};
</script><style scoped lang="scss">
</style>
调用:
<DialogCode ref="dialogCode" />
// 调用methods: {showCode() {this.$refs.dialogCode.showDialog = true;},}