原生javascript实现canvas图形验证码

  感觉自己最近好像很少拿原生js写东西了,自己的js水平也还有待提高。上周做原型设计的时候设计了一个图形验证码,感觉这么小一个东西也没必要去找个库,就打算自己造个小轮子来用了。

  图形验证码的作用是一种人机识别的手段,目的是为了区分正常人和机器人,所以没必要设计的太麻烦(12306之前的验证码简直!!),所以就采用数字+大小写字母共4个字符进行验证,用canvas进行绘制,如果只是简单这样也太简单了,像接触到的其他网站使用的验证码还有背景色和干扰的线条,基本上这就是实现的思路了。


  根据实现的思路,随机颜色的方法、生成随机数的方法肯定是需要的。

/*
 * 生成一个随机数
 */
function randomNum(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
}

/*
 * 生成随机颜色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})`;
}

  在html我们只要需要提供一个容器就可以了,调用时传容器id,以及图形验证码的size就可以了。

<div id="verifyContainer"></div>

  获取到容器和size之后,我们得先添加一个canvas,参考了下网上其他验证码的背景色大概都在160-240左右可能这个区间的颜色会偏浅一点。

/*
 * 向html添加canvas
 */
function appendCanvas(id, width, height) {
    const container = document.getElementById(id);
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    canvas.style.backgroundColor = randomColor(160, 240);
    container.appendChild(canvas);
}

  然后再定义两个常量及大小写字符数组和数字数组

const numberArr = "0,1,2,3,4,5,6,7,8,9".split(',');
const characterArr = "A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z".split(',');

  然后就可以开始绘制验证码了

/**
 * 绘制验证码
 */
function drawCharacter(id, width, height) {
    const canvas = document.getElementById(id).getElementsByTagName('canvas')[0];
    if (canvas.getContext) {
        var ctx = canvas.getContext('2d');
    }
    ctx.textBaseline = 'middle';
    ctx.font = '20px SimHei';

    // 开始绘制
    for (let i = 1; i <= 4; i++) {
        var character = textArr[randomNum(0, textArr.length)];
        // 随机字体颜色
        ctx.fillStyle = randomColor(50, 160);
        ctx.shadowBlur = randomNum(-3, 3);
        ctx.shadowColor = "rgba(0, 0, 0, 0.3)";
        var x = width / 5 * i;
        var y = height / 2;
        var deg = randomNum(-30, 30);
        // 设置旋转角度和坐标原点
        ctx.translate(x, y);
        ctx.rotate(deg * Math.PI / 180);
        ctx.fillText(character, 0 , 0);
        // 恢复旋转角度和原点
        ctx.rotate(-deg * Math.PI / 180);
        ctx.translate(-x, -y);
    }
}

  这样验证码就绘制出来了,但是确实太简单了一点,需要在加一点难度,加一些干扰线和干扰点。干扰线的话影响会较大一点,所以要少一点字符数量为宜,干扰点可以多一些,大概在canvas宽度1/5的样子。

/**
 * 绘制干扰线
 */
function interferenceLine(ctx, width, height) {
    for ( let i= 0; i < 4; i++) {
        ctx.strokeStyle = randomColor(40, 180);
        ctx.beginPath();
        ctx.moveTo(randomNum(0, width / 2), randomNum(0, height / 2));
        ctx.lineTo(randomNum(0, width / 2), randomNum(0, height / 2));
        ctx.stroke();
    }
}

/**
 * 绘制干扰点
 */
function interferenceDot(ctx, width, height) {
    for (let i = 0; i < width / 5; i++) {
        ctx.fillStyle = randomColor(0, 255);
        ctx.beginPath();
        ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);
        ctx.fill();
    }
}

  总的说来实现的比较粗糙,代码也没有去优化,本来还可以添加一些比较人性化的定制功能的,因为忙着到处看房子去了,没来得及弄,打算后面花时间优化一下。

  代码和在线演示分享在jsfiddle上,check.