用canvas创建一个简单的验证码生成器

854 阅读5分钟

前言

图形验证码是一种常见的安全措施,用于区分人类用户和自动化软件(如机器人)。它通常以扭曲的字母和数字形式出现,这些字母和数字难以被光学字符识别技术解析。通过要求用户正确输入这些字符,网站能够有效防止垃圾邮件发送者、恶意登录尝试等行为。

今天向大家介绍如何使用canvas来创建一个简单的图形验证码生成器。实现的效果如下:

8ddb380cdb5fa024e0e4439a804ea881.gif

正文

HTML结构

首先,让我们来看一下HTML部分:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>验证码生成器</title>
</head>
<body>
    <canvas id="canvas" width="120" height="40" onclick="draw()"></canvas>

    <script>
        // JavaScript代码将在这里
    </script>
</body>
</html>

解释:我们实现这个验证码最关键的元素是<canvas>标签。canvas是一个HTML5元素,允许我们在网页上绘制图形。我们设置了它的宽度为120像素,高度为40像素,并且给它绑定了一个onclick事件处理函数draw() 。这意味着每当用户点击这个canvas时,都会调用draw() 函数来重新生成验证码。

JavaScript逻辑

接下来,我们来详细了解JavaScript代码。

随机数生成

首先,我们需要能够生成随机数。为此,我们定义了randomNum()函数,它接收两个参数:最小值min和最大值max,然后返回在这两个值之间的随机整数,用于后面生成随机的背景颜色和验证码里面字符。

function randomNum(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
}

解释Math.random() 生成0到1之间的数,我们通过乘以 (max - min)再加上min 就是一个在max和min之间的数了(生成0是最小的数,生成1是最大的数),最后使用Math.floor() 取整,因为Math.random()生成的数字是带很多位的小数所有这里取整,向下取整或向上取整都无所谓,反正是生成随机数嘛。

随机颜色生成

接下来,我们需要生成随机颜色。我们定义了randomColor()函数,它也接受最小值和最大值作为参数,并返回一个随机的RGB颜色字符串,用于后面随机生成验证码的背景颜色。

function randomColor(min, max) {
    const r = randomNum(min, max);
    const g = randomNum(min, max);
    const b = randomNum(min, max);
    return `rgb(${r},${g},${b})`;
}

解释:在这个函数中,我们分别生成红色、绿色和蓝色的随机值(即r、g、b),并将它们组合成一个RGB颜色字符串。

绘制验证码

现在我们来编写draw()函数,它将执行以下操作:

  1. 填充背景颜色。
  2. 在随机位置和角度上绘制随机字符。
  3. 绘制干扰线条。
  4. 绘制干扰点。
获取画布和上下文

首先,我们需要获取canvas元素,并获取其2D绘图上下文,这样我们就可以开始在画布上绘制了。

let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

解释getContext('2d')方法返回一个CanvasRenderingContext2D对象,我们可以使用它来在画布上绘制。

背景颜色

我们首先设置一个随机的颜色作为背景色,并用该颜色填充整个画布。

ctx.fillStyle = randomColor(180, 230); // 设置填充颜色
ctx.fillRect(0, 0, canvas.width, canvas.height); // 用颜色填充整个画布

解释fillStyle属性用于设置填充图形的颜色。fillRect(x, y, w, h)方法用于绘制一个填充矩形。

效果如下

image.png

字符绘制

接着,我们生成四个随机字符,并在每个字符上应用随机的字体大小、旋转角度和颜色。

const pool = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'; // 验证码的字符

for (let i = 0; i < 4; i++) {
    const text = pool[randomNum(0, pool.length)]; // 随机选择一个字符
    const fontSize = randomNum(18, 40); // 随机字体大小
    const deg = randomNum(-30, 30); // 随机旋转角度
    ctx.font = `${fontSize}px SimHei`; // 设置字体和大小
    ctx.textBaseline = 'top'; // 文本基线设为顶部
    ctx.fillStyle = randomColor(80, 150); // 设置填充颜色
    ctx.save(); // 保存当前状态
    ctx.translate(30 * i + 15, 15); // 移动坐标原点
    ctx.rotate((deg * Math.PI) / 180); // 旋转画布
    ctx.fillText(text, -10, -15); // 绘制文本
    ctx.restore(); // 恢复之前的状态
}

解释

  • font:用于设置文本的字体和大小。
  • textBaseline:用于设置文本基线的位置,使验证码的字母的头部对齐。
  • fillText(text, x, y):用于在指定位置绘制文本。
  • save():当前状态封存入栈。
  • translate(x, y):把字母平移到相对的位置。
  • rotate():将字母旋转,现实中我们看到的字母都是歪七扭八的对吧。
  • restore():恢复出栈,可以继续画别的东西。

效果如下

image.png

干扰线条

为了增加复杂度,我们绘制一些随机的线条,这些线条也将具有随机的颜色。

for (let i = 0; i < 5; i++) {
    ctx.beginPath(); // 开始一条新的路径
    ctx.moveTo(randomNum(0, canvas.width), randomNum(0, canvas.height)); // 下笔位置
    ctx.lineTo(randomNum(0, canvas.width), randomNum(0, canvas.height)); // 结束位置
    ctx.strokeStyle = randomColor(100, 230); // 设置线条颜色
    ctx.closePath(); // 闭合路径
    ctx.stroke(); // 绘制线条
}

解释

  • beginPath():开始一个新的路径。
  • moveTo(x, y):将绘制起点移动到指定位置。
  • lineTo(x, y):用于从当前位置画一条直线到指定位置。
  • strokeStyle:用于设置描边的颜色。
  • closePath():用于关闭当前路径,不然下一条线就会从上一条线开始。
  • stroke():用于绘制当前路径。
干扰点

最后,我们添加一些随机分布的小点作为额外的干扰因素。

for (let i = 0; i < 40; i++) {
    ctx.beginPath(); // 开始一条新的路径
    ctx.arc(randomNum(0, canvas.width), randomNum(0, canvas.height), 1, 0, 2 * Math.PI); // 绘制一个圆形
    ctx.fillStyle = randomColor(150, 200); // 设置填充颜色
    ctx.closePath(); // 闭合路径
    ctx.fill(); // 填充圆形
}

解释

  • arc(x, y, radius, startAngle, endAngle):用于绘制一个弧线,我们这里把画一个小圆形再填充一下就是小圆点了对吧。
  • fill()方法用于填充当前路径,变成一个实现有颜色的圆。

效果如下

image.png

完整代码奉上

将所有这些放在一起,完整的代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <canvas id="canvas" width="120" height="40" onclick="draw()"></canvas>

    <script>
        let pool='ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'

        function randomNum(min,max){
            return Math.floor(Math.random()*(max-min)+min)
        }


        function randomColor(min,max){
            const r=randomNum(min,max)
            const g=randomNum(min,max)
            const b=randomNum(min,max)
            return `rgb(${r},${g},${b})`
        }


        function draw(){
            let canvas=document.getElementById('canvas')
            let ctx=canvas.getContext('2d')

            // 填充色随机
            ctx.fillStyle=randomColor(180,230)
            ctx.fillRect(0,0,canvas.width,canvas.height)

            //随机生成字符串
            for(let i=0;i<4;i++){
                const text=pool[randomNum(0,pool.length)]
                //随机字体大小
                const fontSize=randomNum(18,40)
                //随机旋转角度
                const deg=randomNum(-30,30)
                ctx.font=`${fontSize}px SimHei`
                ctx.textBaseline='top'
                ctx.fillStyle=randomColor(80,150)
                ctx.save()   //当前状态封存入栈
                ctx.translate(30*i+15,15)
                ctx.rotate((deg*Math.PI)/180)
                ctx.fillText(text,-10,-15)
                ctx.restore()  //恢复出栈,可以继续画别的东西
            }

            //随机生成干扰线条
            for(let i=0;i<5;i++){
                ctx.beginPath() //开始一条路径
                ctx.moveTo(randomNum(0,canvas.width),randomNum(0,canvas.height))  //下笔
                ctx.lineTo(randomNum(0,canvas.width),randomNum(0,canvas.height))  //移动
                ctx.strokeStyle=randomColor(100,230)
                ctx.closePath()  //闭合路径,不然下一条线就会从上一条线开始
                ctx.stroke()
            }

            //随机生成干扰点
            for(let i=0;i<40;i++){
                ctx.beginPath()
                ctx.arc(randomNum(0,canvas.width),randomNum(0,canvas.height),1,0,2*Math.PI)  //(x,y(圆心),半径,起始弧度,旋转角度)
                ctx.fillStyle=randomColor(150,200)
                ctx.closePath()
                ctx.fill()
            }
        }
        draw()
    </script>
</body>
</html>

总结

通过上述步骤,我们成功地创建了一个简单的图形验证码生成器。您可以进一步优化和扩展此代码,实现一个更好的验证码。


文章到这里就结束了,希望对你有所帮助,感谢你的阅读!