JavaScript Canvas实现验证码


Posted in Javascript onAugust 02, 2020

在通常的登录界面我们都可以看到验证码,验证码的作用是检测是不是人在操作,防止机器等非人操作,防止数据库被轻而易举的攻破。

验证码一般用PHP和java等后端语言编写。

但是在前端,用canva或者SVG也可以绘制验证码。

绘制验证码不能是简单的随机字符串,而应该在绘制界面有一些干扰项:

如:干扰线段、干扰圆点、背景等等。

这里的这个demo的canvas验证码干扰项比较简单。

可以在图示中看到本例中的干扰项。

canvas验证码展示效果:

点击实现改变(重绘)验证码:

JavaScript Canvas实现验证码

在控制台运行函数输出返回值(验证码):

JavaScript Canvas实现验证码

源码 :

<!DOCTYPE html>
<html lang="en">

<head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <meta http-equiv="X-UA-Compatible" content="ie=edge">
 <title>canvas验证码</title>
</head>

<body>
 <canvas width="200" height="60" id="check" style="border:1px solid #000;">您的浏览器不支持canvas标签!</canvas>
 <script>
 var ctx = document.getElementById("check").getContext("2d");
 var ctxW = document.getElementById("check").clientWidth;
 var ctxH = document.getElementById("check").clientHeight;

 /**
  * 产生一个随机数 可设置随机数区间
  * @param {[Number]} min [随机数区间下限]
  * @param {[Number]} max [随机数区间上限]
  * @return {[Number]} [返回一个在此区间的随机数]
  */
 function ranNum(min, max) {

  return Math.random() * (max - min) + min;

 }

 /**
  * 返回一个随机颜色 可设置颜色区间
  * @param {[Number]} min [颜色下限]
  * @param {[Number]} max [颜色上限]
  * @return {[String]} [随机颜色]
  */
 function ranColor(min, max) {

  var r = ranNum(min, max);

  var g = ranNum(min, max);

  var b = ranNum(min, max);

  // return "rgb(" + r + "," + g + "," + b + ")";
  return `rgb(${r},${g},${b})`;

 }

 /**
  * 随机字符串数组
  * @return {[Array]} [随机数组]
  */
 function ranStr() {

  var str = "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789";

  return str.split("").sort(function () {
  return Math.random() - 0.5
  });

 }

 /**
  * 绘制文本字符串
  * @param {[String]} canvasId [canvas的id]
  * @param {[Number]} canvasW [canvas的width]
  * @param {[Number]} canvasH [canvas的height]
  * @param {[Number]} num [绘制验证码的字数]
  * @param {[Number]} fsMin [字体大小下限]
  * @param {[Number]} fsMax [字体大小上限]
  * @param {[Number]} frMin [字体旋转偏移下限]
  * @param {[Number]} frMax [字体旋转偏移上限]
  * @param {[Number]} min [颜色下限]
  * @param {[Number]} max [颜色上限]
  * @return {[String]} [随机字符串]
  */
 function drawText(canvasId, canvasW, canvasH, num, fsMin, fsMax, frMin, frMax, min, max) {

  var str = "";

  for (var i = 0; i < num; i++) {

  var char = ranStr()[Math.floor(0, ranStr().length)];

  var fs = ranNum(fsMin, fsMax);

  canvasId.font = fs + "px Verdana";

  canvasId.fillStyle = ranColor(min, max);

  // 保存绘制的状态
  canvasId.save();

  // context.translate(x,y);
  // x 添加到水平坐标(x)上的值
  // y 添加到垂直坐标(y)上的值
  // 偏移
  canvasId.translate(canvasW / num * i + canvasW / 20, 0);

  // 变换角度
  canvasId.rotate(ranNum(frMin, frMax) * Math.PI / 180);

  // context.fillText(text,x,y,maxWidth);
  // text 规定在画布上输出的文本。
  // x 开始绘制文本的 x 坐标位置(相对于画布)。
  // y 开始绘制文本的 y 坐标位置(相对于画布)。
  // maxWidth 可选。允许的最大文本宽度,以像素计。
  canvasId.fillText(char, 0, (canvasH + fs) / 2.5, canvasW / num);

  // 返回之前保存过的路径状态和属性
  ctx.restore();

  str += char;

  }

  // console.log(str);
  return str;

 }

 /**
  * 绘制背景
  * @param {[String]} canvasId [canvas的id]
  * @param {[Number]} canvasW [canvas的width]
  * @param {[Number]} canvasH [canvas的height]
  * @param {[Number]} min [下限]
  * @param {[Number]} max [上限]
  */
 function drawBg(canvasId, canvasW, canvasH, min, max) {

  // 绘制canvas背景
  canvasId.fillStyle = ranColor(min, max);

  // 填充颜色
  canvasId.fillRect(0, 0, canvasW, canvasH);

 }

 /**
  * 绘制干扰 圆点
  * @param {[String]} canvasId [canvas的id]
  * @param {[Number]} canvasW [canvas的width]
  * @param {[Number]} canvasH [canvas的height]
  * @param {[Number]} num [绘制的数量]
  * @param {[Number]} r [圆点半径]
  * @param {[Number]} min [下限]
  * @param {[Number]} max [上线]
  */
 function drawCircle(canvasId, canvasW, canvasH, num, r, min, max) {

  for (var i = 0; i < num; i++) {

  // 开始绘制 (拿起笔)
  canvasId.beginPath();

  // context.arc(x,y,r,sAngle,eAngle,counterclockwise); (绘制)
  // x 圆的中心的 x 坐标。
  // y 圆的中心的 y 坐标。
  // r 圆的半径。
  // sAngle 起始角,以弧度计。(弧的圆形的三点钟位置是 0 度)。
  // eAngle 结束角,以弧度计。
  // counterclockwise 可选。规定应该逆时针还是顺时针绘图。False = 顺时针,true = 逆时针。
  canvasId.arc(ranNum(0, canvasW), ranNum(0, canvasH), r, 0, 2 * Math.PI);

  // 填充颜色
  canvasId.fillStyle = ranColor(min, max);

  // 填充
  canvasId.fill();

  // 闭合绘制 (放开笔)
  canvasId.closePath();

  }

 }

 /**
  * 绘制干扰 线段
  * @param {[String]} canvasId [canvas的id]
  * @param {[Number]} canvasW [canvas的width]
  * @param {[Number]} canvasH [canvas的height]
  * @param {[Number]} num [绘制的数量]
  * @param {[Number]} min [下限]
  * @param {[Number]} max [上线]
  */
 function drawLine(canvasId, canvasW, canvasH, num, min, max) {

  for (var i = 0; i < num; i++) {

  // 开始绘制 (拿起笔)
  canvasId.beginPath();

  // 绘制开始点
  canvasId.moveTo(ranNum(0, canvasW), ranNum(0, canvasH));

  // 绘制结束点
  canvasId.lineTo(ranNum(0, canvasW), ranNum(0, canvasH));

  canvasId.strokeStyle = ranColor(min, max);

  canvasId.stroke();

  canvasId.closePath();

  }

 }

 // 绘制验证码
 function drawCanvas() {

  // 清空canvas
  ctx.clearRect(0, 0, 200, 60);

  // 绘制背景
  drawBg(ctx, ctxW, ctxH, 200, 255);

  // 绘制干扰圆点
  drawCircle(ctx, ctxW, ctxH, 20, 5, 200, 255);

  // 绘制干扰线段
  drawLine(ctx, ctxW, ctxH, 20, 0, 255);

  // 绘制验证码
  var str = drawText(ctx, ctxW, ctxH, 4, 10, 50, -30, 30, 0, 100);

  return str;

 }

 drawCanvas();

 document.getElementById('check').onclick = drawCanvas;
 </script>
</body>

</html>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Javascript 设计模式(二) 闭包
May 26 Javascript
javascript弹出层输入框(示例代码)
Dec 11 Javascript
jQuery实现html元素拖拽
Jul 21 Javascript
javascript实现简单的分页特效
Aug 12 Javascript
js采用concat和sort将N个数组拼接起来的方法
Jan 21 Javascript
JS+CSS3制作炫酷的弹窗效果
Nov 08 Javascript
JavaScript仿聊天室聊天记录
Dec 27 Javascript
Three.js开发实现3D地图的实践过程总结
Nov 20 Javascript
vue单页面应用打开新窗口显示跳转页面的实例
Sep 21 Javascript
快速解决layui弹窗按enter键不停弹窗的问题
Sep 18 Javascript
JavaScript进制转换实现方法解析
Jan 18 Javascript
vue3.0自定义指令(drectives)知识点总结
Dec 27 Vue.js
JS实现方形抽奖效果
Aug 27 #Javascript
对vuejs的v-for遍历、v-bind动态改变值、v-if进行判断的实例讲解
Aug 27 #Javascript
Vuex 在Vue 组件中获得Vuex 状态state的方法
Aug 27 #Javascript
JS实现匀速与减速缓慢运动的动画效果封装示例
Aug 27 #Javascript
vue展示dicom文件医疗系统的实现代码
Aug 27 #Javascript
vue中子组件的methods中获取到props中的值方法
Aug 27 #Javascript
最适应的vue.js的form提交涉及多种插件【推荐】
Aug 27 #Javascript
You might like
Mysql中分页查询的两个解决方法比较
2013/05/02 PHP
JavaScript 申明函数的三种方法 每个函数就是一个对象(一)
2009/12/04 Javascript
JavaScript中常见陷阱小结
2010/04/27 Javascript
基于jquery的loading 加载提示效果实现代码
2011/09/01 Javascript
JS注册/移除事件处理程序(ExtJS应用程序设计实战)
2013/05/07 Javascript
javascript调试过程中找不到哪里出错的可能原因
2013/12/16 Javascript
解决用jquery load加载页面到div时,不执行页面js的问题
2014/02/22 Javascript
jQuery中:text选择器用法实例
2015/01/03 Javascript
JS实现网页上随机产生超链接地址的方法
2015/11/09 Javascript
轻松学习jQuery插件EasyUI EasyUI创建RSS Feed阅读器
2015/11/30 Javascript
jQuery实现拖拽页面元素并将其保存到cookie的方法
2016/06/12 Javascript
微信小程序 Video API实例详解
2016/10/02 Javascript
js学习笔记之事件处理模型
2016/10/31 Javascript
原生js实现手风琴功能(支持横纵向调用)
2017/01/13 Javascript
浅谈webpack打包生成的bundle.js文件过大的问题
2018/02/22 Javascript
jQuery实现的中英文切换功能示例
2019/01/11 jQuery
Electron整合React使用搭建开发环境的步骤详解
2020/06/07 Javascript
解决vue 给window添加和移除resize事件遇到的坑
2020/07/21 Javascript
Python 实现文件的全备份和差异备份详解
2016/12/27 Python
关于Python数据结构中字典的心得
2017/12/04 Python
python3+PyQt5实现自定义窗口部件Counters
2018/04/20 Python
python实现简单tftp(基于udp协议)
2018/07/30 Python
Django 数据库同步操作技巧详解
2019/07/19 Python
python-numpy-指数分布实例详解
2019/12/07 Python
详解Tensorflow不同版本要求与CUDA及CUDNN版本对应关系
2020/08/04 Python
瑞士首家网上药店折扣店:McDrogerie
2020/12/22 全球购物
外贸业务员求职自荐信分享
2013/09/21 职场文书
装饰资料员岗位职责
2013/12/30 职场文书
工商企业管理应届生求职信
2014/05/04 职场文书
不服从公司安排检讨书
2014/09/24 职场文书
中秋节随笔
2015/08/15 职场文书
MySQL中distinct与group by之间的性能进行比较
2021/05/26 MySQL
与Windows10相比Windows11有哪些改进?值不值得升级?
2021/11/21 数码科技
JavaScript实例 ODO List分析
2022/01/22 Javascript
阿里云日志过滤器配置日志服务
2022/04/09 Servers
在虚拟机中安装windows server 2008的图文教程
2022/06/28 Servers