canvas制作表单验证码

canvas是个非常强大的组件,网页上的验证码一般都是用服务器语言制作出来的

canvas同样是可以实现这个功能的

下面请观看效果图:

步骤呢其实也很简单

HTML部分:      

1 <form action="" id="form" method="post" onsubmit="check()">
2     <label for="chat">请输入验证码:</label>
3     <input type="text" id="chat">
4     <canvas id="myCanvas" width="120" height="40"></canvas>
5     <button type="submit" id="btn">提交</button>
6 </form>

CSS部分:

 1 form{
 2     width: 400px;
 3     display: flex;
 4     align-items: center;
 5     justify-content: space-between;
 6  }
 7 #chat{
 8      width: 100px;
 9      height: 30px;
10      outline: none;
11      font-size: 20px;
12 }

简单设置好表单的样式后,开始制作canvas

这是全部js代码

  1 <script>
  2         let c = document.getElementById('myCanvas');
  3         let ctx = c.getContext('2d');
  4         let chat = document.getElementById('chat');
  5         let form = document.getElementById('form');
  6 
  7         function fun(){
  8             let draw = {
  9                 bgColor : `rgb(${255},${255},${255})`,
 10                 dot : {
 11                     num : 20,//点的个数
 12                     radius : 1//点的半径
 13                 },
 14                 line : {
 15                     num : 10,//线的个数
 16                     width : 1 //线的宽度
 17                 },
 18                 code : {
 19                     num : 4,//字母个数
 20                     text : [],//存放字母
 21                     deg : [],//存放字母旋转角度
 22                     size : 25,//字体大小
 23                     maxWidth : 20//字体最大宽度
 24                 }
 25             };
 26 
 27             //设置画布的背景颜色
 28             ctx.fillStyle = draw.bgColor;
 29             ctx.fillRect(0, 0, c.width, c.height);
 30 
 31             let x1 = null;
 32             let y1 = null;
 33             let x2 = null;
 34             let y2 = null;
 35             let color = null;
 36             let choose = null;
 37             let code = null;
 38 
 39             //小写字母的ascii码97~122,大写65~90
 40             for( let i = 0; i < draw.code.num; i ++ ){
 41                 choose = Math.floor(Math.random() * 2);
 42                 if(choose === 0){
 43                     code = Math.floor(Math.random() * 26) + 97;
 44                 }else{
 45                     code = Math.floor(Math.random() * 26) + 65;
 46                 }
 47                 draw.code.text.push(String.fromCharCode(code));
 48                 draw.code.deg.push(Math.floor(Math.random() * 61) - 30);//倾斜角度从-30到30
 49             }
 50 
 51             for( let i = 0; i < draw.code.num; i ++ ){
 52                 x1 = 10 + i * draw.code.size;
 53                 y1 = (c.height - draw.code.size) / 2 + draw.code.size;
 54                 color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`;
 55 
 56                 //绘制字母
 57                 ctx.save();
 58                 ctx.beginPath();
 59                 ctx.fillStyle = color;
 60                 ctx.font = `${draw.code.size}px Microsoft YaHei`;
 61                 ctx.translate(x1 + draw.code.maxWidth / 2,y1 - draw.code.size / 2);
 62                 ctx.rotate(draw.code.deg[i] * Math.PI/180);
 63                 ctx.fillText(draw.code.text[i], - draw.code.maxWidth / 2, + draw.code.size / 2, draw.code.maxWidth);
 64                 ctx.restore();
 65             }
 66 
 67             for( let i = 0; i < draw.dot.num; i ++ ){
 68                 x1 = Math.random() * (c.width - draw.dot.radius) + draw.dot.radius;//随机点的初始横坐标
 69                 y1 = Math.random() * (c.height - draw.dot.radius) + draw.dot.radius;//随机点的初始纵坐标
 70                 color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`;//随机点的颜色
 71 
 72                 //绘制点
 73                 ctx.beginPath();
 74                 ctx.fillStyle = color;
 75                 ctx.arc( x1, y1, draw.dot.radius, 0, Math.PI * 2 );
 76                 ctx.fill();
 77             }
 78 
 79             for( let i = 0; i < draw.line.num; i ++ ){
 80                 x1 = Math.random() * c.width;//线的开始横坐标
 81                 y1 = Math.random() * c.height;//线的开始纵坐标
 82                 x2 = Math.random() * c.width;//线的结束横坐标
 83                 y2 = Math.random() * c.height;//线的结束纵坐标
 84                 90                 color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`;
 91 
 92                 //绘制线
 93                 ctx.beginPath();
 94                 ctx.lineWidth = draw.line.width;
 95                 ctx.strokeStyle = color;
 96                 ctx.moveTo(x1, y1);
 97                 ctx.lineTo(x2, y2);
 98                 ctx.stroke();
 99             }
100            return(draw.code.text);//返回数组用于验证
101         }
102 
103         fun();
104 
105         let text = fun();
106 
107         function check(){
108             form.action = chat.value === text.join('') ? 'https://www.baidu.com/' : '#';
109         }
110     </script>

下面我一点点说:

 1 let draw = {
 2                 bgColor : `rgb(${255},${255},${255})`,
 3                 dot : {
 4                     num : 20,//点的个数
 5                     radius : 1//点的半径
 6                 },
 7                 line : {
 8                     num : 10,//线的个数
 9                     width : 1 //线的宽度
10                 },
11                 code : {
12                     num : 4,//字母个数
13                     text : [],//存放字母
14                     deg : [],//存放字母旋转角度
15                     size : 25,//字体大小
16                     maxWidth : 20//字体最大宽度
17                 }
18             };

定义一个JSON数组来存放绘图所需的变量

1 ctx.fillStyle = draw.bgColor;
2 ctx.fillRect(0, 0, c.width, c.height);

设置画布背景颜色

1 let x1 = null;
2 let y1 = null;
3 let x2 = null;
4 let y2 = null;
5 let color = null;
6 let choose = null;
7 let code = null;

一些绘图所需的临时变量

我个人比较倾向于先绘制字母,再绘制点和线,这样干扰性更强

 1 for( let i = 0; i < draw.code.num; i ++ ){
 2     choose = Math.floor(Math.random() * 2);
 3     if(choose === 0){
 4         code = Math.floor(Math.random() * 26) + 97;
 5     }else{
 6         code = Math.floor(Math.random() * 26) + 65;
 7     }
 8     draw.code.text.push(String.fromCharCode(code));
 9     draw.code.deg.push(Math.floor(Math.random() * 61) - 30);//倾斜角度从-30到30
10 }

首先绘制字母,字母有分大写和小写,这里我就用随机数来决定大写和小

小写字母的ascii码值97~122,大写65~90

将随机出的字母和随机出的(字母旋转角度)推入数组

 1 for( let i = 0; i < draw.code.num; i ++ ){
 2     x1 = 10 + i * draw.code.size;
 3     y1 = (c.height - draw.code.size) / 2 + draw.code.size;
 4     color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`;
 5 
 6     //绘制字母
 7     ctx.save();
 8     ctx.beginPath();
 9     ctx.fillStyle = color;
10     ctx.font = `${draw.code.size}px Microsoft YaHei`;
11     ctx.translate(x1 + draw.code.maxWidth / 2,y1 - draw.code.size / 2);
12     ctx.rotate(draw.code.deg[i] * Math.PI/180);
13     ctx.fillText(draw.code.text[i], - draw.code.maxWidth / 2, + draw.code.size / 2, draw.code.maxWidth);
14     ctx.restore();
15 }

x1是字母绘制的起始横坐标,y1是起始纵坐标

注意:绘制字符和绘制图形是不一样的

字符的起始点位于字符的左下角,而图形的起始点位于图形的左上角

计算出起始坐标后,随机出字母的填充颜色

ctx.save();储存当前画笔信息,此时画笔的translate值为0,0,rotate值为0

字符的起始点是位于左下角的,而我们想要的旋转效果是绕着字符中心旋转,起始点就应该设置在字符中心

那么横坐标需要加上字母的1/2宽度,而纵坐标需要减去字母的高度(也就是字母的字体大小)

ctx.translate(x1 + draw.code.maxWidth / 2,y1 - draw.code.size / 2);

旋转字体,常规操作

ctx.rotate(draw.code.deg[i] * Math.PI/180);

这时候再减去刚刚变化的坐标值,回到原来的起始点

ctx.fillText(draw.code.text[i], - draw.code.maxWidth / 2, + draw.code.size / 2, draw.code.maxWidth);

ctx.restore();将画笔的信息重置回储存时的,也就是translate(0,0)、rotate(0),便于绘制下一个字母

 1 for( let i = 0; i < draw.dot.num; i ++ ){
 2     x1 = Math.random() * (c.width - draw.dot.radius) + draw.dot.radius;//随机点的初始横坐标
 3     y1 = Math.random() * (c.height - draw.dot.radius) + draw.dot.radius;//随机点的初始纵坐标
 4     color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`;//随机点的颜色
 5 
 6     //绘制点
 7     ctx.beginPath();
 8     ctx.fillStyle = color;
 9     ctx.arc( x1, y1, draw.dot.radius, 0, Math.PI * 2 );
10     ctx.fill();
11 }

绘制点,随机出点的初始横坐标和纵坐标,仍然用x1,y1,因为字母绘制完已经不需要x1,y1了,无需再定义新的变量

 1 for( let i = 0; i < draw.line.num; i ++ ){
 2     x1 = Math.random() * c.width;//线的开始横坐标
 3     y1 = Math.random() * c.height;//线的开始纵坐标
 4     x2 = Math.random() * c.width;//线的结束横坐标
 5     y2 = Math.random() * c.height;//线的结束纵坐标12     color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`;
13 
14     //绘制线
15     ctx.beginPath();
16     ctx.lineWidth = draw.line.width;
17     ctx.strokeStyle = color;
18     ctx.moveTo(x1, y1);
19     ctx.lineTo(x2, y2);
20     ctx.stroke();
21 }

最后绘制直线,仍然使用x1,y1做初始坐标,添加x2,y2做结束坐标

然后我们需要验证我们在输入框中输入的验证码是否正确

1 return(draw.code.text);

返回储存在数组中的字母

1 fun();
2 let text = fun();

运行fun()函数,再令text = fun();

两个操作不能调换顺序,因为每次运行fun()得到的结果都是不一样的

1 function check(){
2     form.action = chat.value === text.join('') ? 'https://www.baidu.com/' : '#';
3 }

定义一个check函数来验证我们在输入框中输入的验证码是否正确

如果正确就跳到百度,不正确就刷新页面

但要注意的是前面html中的form标签要设置一个属性onsubmit="check()"

这样点击提交按钮的时候就会无视其他form设置的属性,先执行check函数,验证是否正确。

如果不设置onsubmit="check()",那么按照优先级顺序,会先执行form的action属性,会刷新页面,此时又会运行一次fun(),验证码就对不上了

这里只是做一个简单的验证码,包括字母 + 数字,字符变形,大小写均可这些效果我就不做了。

原文地址:https://www.cnblogs.com/FrankLongger/p/9621912.html