关于前端:上班划水无聊写了个JS生成图片验证码

28次阅读

共计 2883 个字符,预计需要花费 8 分钟才能阅读完成。

写本文的初衷是纯正为 cavans 练手,理论工作当中并不倡议应用前端生成的图片验证码;前端应用验证码的初衷就是为了避免用户谬误申请接口,也为了躲避居心叵测之人频繁申请接口对服务器进行攻打;因而如果后端能提供验证码接口则尽量应用后端提供的接口

场景剖析

随着时代的提高咱们可能曾经很久没遇到过图片验证码了,当初支流的验证形式基本上为短信,邮箱,以及拖动验证;然而并不障碍咱们学习它,实现图片验证码的次要形式是 cavans,咱们能够借此机会学习一下 cavans;

需要剖析

你还记得上一次见到图片验证码是什么时候吗?你还记得一个图片验证码是由哪些元素组成的吗?
我总结下来就只有两点:

  • 字符串
  • 烦扰元素

而字符串与烦扰元素也领有多重属性比方:

  • 绝对偏移量
  • 字体
  • 字体色彩
  • 字体大小
  • 暗影成果
  • 字符串长度

而字符串与烦扰元素是由图片承载的,图片也领有宽高以及背景色

而对于一个残缺的需要而言还须要有一些交互比方:

  • 图片的生成
  • 图片的刷新
  • 验证码的比对

实现

接下来咱们进行简略的实现,通过下面的剖析咱们大抵晓得了咱们须要属性因而接下来咱们能够创立

class Verification{constructor (length=6,style={width:200,height:30,hasDot:true,hasLine:true}) {
        this.code = null// 随机 code 字符串
        this.path = null// 生成的图片地址
        this.length = length// 验证码长度
        this.width = style.width// 图片宽度
        this.height = style.height// 图片高度
        this.hasDot = style.hasDot// 是否有烦扰点
        this.hasLine = style.hasLine// 是否有烦扰线
        this.create()}
    create(){
        // 创立随机字符串
        const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
        const maxLength = chars.length
        let code = ''
        for (let i = 0; i < this.length; i++) {code = code + chars.charAt(Math.floor(Math.random() * maxLength))
        }
        this.code = code
        // 构建 canvas
        this.createPath()}
    // 构建 canvas
    createPath(){
        // 获取随机数
        const getRandomNum=(min, max)=> {return Math.floor(Math.random() * (max - min) + min);
        }
        // 获取随机色
        const getRandomColor=(min, max,a=1)=> {let r = getRandomNum(min, max);
            let g = getRandomNum(min, max);
            let b = getRandomNum(min, max);
            return `rgba(${r},${g},${b},${a})`;
        }
        const canvas = document.createElement('canvas')
        canvas.width = this.width
        canvas.height = this.height
        const ctx = canvas.getContext('2d')

        ctx.textBaseline = "middle";// 设置对其形式

        ctx.fillStyle = getRandomColor(180, 240);// 图片背景色设置,尽量将背景设置成浅色,文字为深色不便浏览
        ctx.fillRect(0, 0, this.width, this.height);// 背景色绘制
        [...this.code].forEach((char,i)=>{ctx.font = getRandomNum(this.height/2, this.height) + 'px pingfang sc'; // 随机生成字体大小
            ctx.fillStyle = getRandomColor(80, 150) // 随机生成字体色彩
            ctx.shadowOffsetX = getRandomNum(-5, 5);// 文字暗影 X 轴偏移量
            ctx.shadowOffsetY = getRandomNum(-5, 5);// 文字暗影 Y 轴偏移量
            ctx.shadowColor = getRandomColor(80, 150,0.4);// 文字暗影色彩
            // 文字渲染 X 偏移量计算
            /*
            * 将图片的 width 除以 code 的长度获取文字在图片中的偏移,防止重叠
            * */
            let x = this.width / this.length * i;
            // 因为文字大小是大于等于 this.height / 2 而小于等于 this.height 所以设置 this.height / 2 可展现全副
            let y = this.height / 2;
            // 文字旋转偏移量
            let deg = getRandomNum(-10, 10);
            // 设置文字偏移
            ctx.translate(x, y);
            // 设置文字旋转
            ctx.rotate(deg * Math.PI / 180);
            // 文字绘制
            ctx.fillText(char, 0, 0);
            // 画布旋转回正
            ctx.rotate(-deg * Math.PI / 180);
            // 画布偏移回正
            ctx.translate(-x, -y);
            // 是否有线
            if (this.hasLine) {ctx.strokeStyle = getRandomColor(60, 160);// 随机设置线色彩
                ctx.beginPath();// 开始门路
                ctx.moveTo(getRandomNum(0, this.width), getRandomNum(0, this.height));// 线终点
                ctx.lineTo(getRandomNum(0, this.width), getRandomNum(0, this.height));// 线起点
                ctx.stroke();// 线绘制}
            // 是否有点
            if(this.hasDot){ctx.fillStyle = getRandomColor(0, 255);// 随机设置点色彩
                ctx.beginPath();// 开始门路
                ctx.arc(getRandomNum(0, this.width), getRandomNum(0, this.height), 1, 0, 2 * Math.PI);// 点设置
                ctx.fill();// 点绘制}
        })

        this.path = canvas.toDataURL("image/png")
    }
    // 验证输出的验证码是否与生成的相等
    equal(code){
        // 均转换为英文大写进行比对
        return code.toUpperCase()===this.code.toUpperCase()
    }
}
const v = new Verification()
document.getElementById('picture').setAttribute("src",v.path)

正文完
 0