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

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

下面请观看效果图:

步骤呢其实也很简单

HTML部分:

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

CSS部分:

 form{
width: 400px;
display: flex;
align-items: center;
justify-content: space-between;
}
#chat{
width: 100px;
height: 30px;
outline: none;
font-size: 20px;
}

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

这是全部js代码

 <script>
let c = document.getElementById('myCanvas');
let ctx = c.getContext('2d');
let chat = document.getElementById('chat');
let form = document.getElementById('form'); function fun(){
let draw = {
bgColor : `rgb(${255},${255},${255})`,
dot : {
num : 20,//点的个数
radius : 1//点的半径
},
line : {
num : 10,//线的个数
width : 1 //线的宽度
},
code : {
num : 4,//字母个数
text : [],//存放字母
deg : [],//存放字母旋转角度
size : 25,//字体大小
maxWidth : 20//字体最大宽度
}
}; //设置画布的背景颜色
ctx.fillStyle = draw.bgColor;
ctx.fillRect(0, 0, c.width, c.height); let x1 = null;
let y1 = null;
let x2 = null;
let y2 = null;
let color = null;
let choose = null;
let code = null; //小写字母的ascii码97~122,大写65~90
for( let i = 0; i < draw.code.num; i ++ ){
choose = Math.floor(Math.random() * 2);
if(choose === 0){
code = Math.floor(Math.random() * 26) + 97;
}else{
code = Math.floor(Math.random() * 26) + 65;
}
draw.code.text.push(String.fromCharCode(code));
draw.code.deg.push(Math.floor(Math.random() * 61) - 30);//倾斜角度从-30到30
} for( let i = 0; i < draw.code.num; i ++ ){
x1 = 10 + i * draw.code.size;
y1 = (c.height - draw.code.size) / 2 + draw.code.size;
color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`; //绘制字母
ctx.save();
ctx.beginPath();
ctx.fillStyle = color;
ctx.font = `${draw.code.size}px Microsoft YaHei`;
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();
} for( let i = 0; i < draw.dot.num; i ++ ){
x1 = Math.random() * (c.width - draw.dot.radius) + draw.dot.radius;//随机点的初始横坐标
y1 = Math.random() * (c.height - draw.dot.radius) + draw.dot.radius;//随机点的初始纵坐标
color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`;//随机点的颜色 //绘制点
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc( x1, y1, draw.dot.radius, 0, Math.PI * 2 );
ctx.fill();
} for( let i = 0; i < draw.line.num; i ++ ){
x1 = Math.random() * c.width;//线的开始横坐标
y1 = Math.random() * c.height;//线的开始纵坐标
x2 = Math.random() * c.width;//线的结束横坐标
y2 = Math.random() * c.height;//线的结束纵坐标
color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`; //绘制线
ctx.beginPath();
ctx.lineWidth = draw.line.width;
ctx.strokeStyle = color;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}
return(draw.code.text);//返回数组用于验证
} fun(); let text = fun(); function check(){
form.action = chat.value === text.join('') ? 'https://www.baidu.com/' : '#';
}
</script>

下面我一点点说:

 let draw = {
bgColor : `rgb(${255},${255},${255})`,
dot : {
num : 20,//点的个数
radius : 1//点的半径
},
line : {
num : 10,//线的个数
width : 1 //线的宽度
},
code : {
num : 4,//字母个数
text : [],//存放字母
deg : [],//存放字母旋转角度
size : 25,//字体大小
maxWidth : 20//字体最大宽度
}
};

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

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

设置画布背景颜色

 let x1 = null;
let y1 = null;
let x2 = null;
let y2 = null;
let color = null;
let choose = null;
let code = null;

一些绘图所需的临时变量

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

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

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

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

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

 for( let i = 0; i < draw.code.num; i ++ ){
x1 = 10 + i * draw.code.size;
y1 = (c.height - draw.code.size) / 2 + draw.code.size;
color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`; //绘制字母
ctx.save();
ctx.beginPath();
ctx.fillStyle = color;
ctx.font = `${draw.code.size}px Microsoft YaHei`;
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();
}

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),便于绘制下一个字母

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

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

 for( let i = 0; i < draw.line.num; i ++ ){
x1 = Math.random() * c.width;//线的开始横坐标
y1 = Math.random() * c.height;//线的开始纵坐标
x2 = Math.random() * c.width;//线的结束横坐标
y2 = Math.random() * c.height;//线的结束纵坐标 color = `rgb(${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)},${Math.floor(Math.random() * 255)})`; //绘制线
ctx.beginPath();
ctx.lineWidth = draw.line.width;
ctx.strokeStyle = color;
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.stroke();
}

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

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

 return(draw.code.text);

返回储存在数组中的字母

 fun();
let text = fun();

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

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

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

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

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

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

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

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

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

canvas制作表单验证码的更多相关文章

  1. ExtJS4.2学习(16)制作表单(转)

    鸣谢:http://www.shuyangyang.com.cn/jishuliangongfang/qianduanjishu/2013-12-10/188.html --------------- ...

  2. 原生js制作表单验证,基本的表单验证方法

    表单验证是web前端最常见的功能之一,也属于前端开发的基本功.自己完成一个表单验证的开发,也有助于加深对字符串处理和正则表达式的理解. 基本的表单验证包括如:字母验证.数字验证.字母和数字验证.汉字验 ...

  3. 如何用ChemFinder制作子表单

    通过使用ChemFinder这一插件,用户可以建立自己的ChemFinder数据库,数据库是由表单集合而成,所以建立数据库的前提是要制作表单.在之前的教程中已经介绍了制作表单的方法,本节ChemDra ...

  4. Thinkphp 表单验证

    在服务器端通过tp框架实现表单验证 用户名.密码.重复密码.邮箱.qq.手机号码.爱好.学历 具体步骤: 制作表单 表单form数据通过create()方法收集(验证功能要求我们必须通过create( ...

  5. Bootstrap框架(基础篇)之列表,表格,表单

    继续上篇的基础部分延伸,主要说一下列表,表格,表单相关Bootstrap框架变化以及基础知识. 1.列表篇 除了HTML提供的三种基本列表样式: 无序列表 <ul> <li>… ...

  6. 详解Bootstrap表单组件

    表单常见的元素主要包括:文本输入框.下拉选择框.单选框.复选框.文本域.按钮等.下面是不同的bootstrap版本: LESS:  forms.less SASS:  _forms.scss boot ...

  7. Bootstrap系列 -- 21. 表单提示信息

    平常在制作表单验证时,要提供不同的提示信息.在Bootstrap框架中也提供了这样的效果.使用了一个"help-block"样式,将提示信息以块状显示,并且显示在控件底部. < ...

  8. Bootstrap_表单

    表单样式 一.基础表单 <form > <div class="form-group"> <label>邮箱:</label> &l ...

  9. 深入浅出ExtJS 第四章 表单与输入控件

    4.1 制作表单 var form = new Ext.form.FormPanel({ title:'form', defaultType:'textfield', buttonAlign:'cen ...

随机推荐

  1. hadoop(三)

    hadoop(三) 1.对MapReduce的认识   MapReduce是运行在yarn上面的一个分布式运算框架,它是用来解决海量的分布式运算的.对于MapReduce来说,我们可以把它分成两部分来 ...

  2. Android UI中英文自动显示问题

    最近做了一个项目,其中有个视频和图片需要添加各种水印(日期,地点,经纬度,用户ID,产品ID等)问题,而且水印还要支持中英文自动切换显示.功能设计和实现算是比较顺利.昨天测试部给了一个小Bug,那就是 ...

  3. VS2010下编译配置Boost_1.53

    一.准备工作 1.下载最新版本的boost库.所在地址:boost_1_53_0.zip.官方推荐7z压缩格式的,因为其压缩效率更好,相应包的大小也比较小. 2.解压缩到指定目录,如C:\boost_ ...

  4. 原生Js封装的产品图片360度展示

    挺简单的一段程序,但是效果不错: 1.把需要展示的36张图片先预加载到浏览器缓存里 2.给展示图片的div添加方法 3.通过鼠标左右移动的像素转换图片 在线效果预览:http://jsfiddle.n ...

  5. 生成sql server 数据库 脚本的 存储过程和调用

    USE [db_datadown] GO /****** Object: StoredProcedure [dbo].[GetTBScript] Script Date: 03/05/2015 09: ...

  6. ORACLE(emp)表习题与答案

    因为答案都是小编自己写的,解法可能有多种,如果您觉得我的解法有误,希望您有时间给我留言. 一.习题 (1) 查询20部门的所有员工信息. SELECT * FROM emp where deptno ...

  7. CS代码代写, 程序代写, java代写, python代写, c/c++代写,csdaixie,daixie,作业代写,代写

    互联网一线工程师程序代写 微信联系 当天完成特色: 互联网一线工程师 24-48小时完成.用心代写/辅导/帮助客户CS作业. 客户反馈与评价 服务质量:保证honor code,代码原创.参考课程sl ...

  8. Spring Boot:快速入门教程

    什么是Spring Boot? Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人 ...

  9. 【POJ - 3414】Pots(bfs)

    Pots 直接上中文 Descriptions: 给你两个容器,分别能装下A升水和B升水,并且可以进行以下操作 FILL(i)        将第i个容器从水龙头里装满(1 ≤ i ≤ 2); DRO ...

  10. PHP学习(一)

    // php注释: // 单行注释 /*多行注释 多行注释*/ /** *姓名:李华 *时间:2016年 *内容:文档注释 */ #这是脚本注释--以下是注释代码 /*php的数据类型: 标量类型(4 ...