本文主要介绍用canvas实现图形验证码的一些思路以及如何用javascript面向对象的方式更友好的实现canvas的功能,关于canvas的一些基本使用方法和API我整理了一个思维导图,大家感兴趣的可以参考学习。

你将收获

  • 闭包的使用

  • canvas常用api的使用

  • javascript面向对象的实现方式

  • 实现一个canvas的图形验证码的一般思路和常用算法

设计思路

  1. 用canvas生成画布

  2. 用canvas画干扰线或躁点

  3. 生成随机不重复的n的字母

  4. 用canvas绘制文字

  5. 初始化和canvas点击事件

  6. 组件化封装

文末将附上组件封装的源码,欢迎大家随时沟通交流。关于项目的打包,我将使用自己基于gulp4搭建的9012教你如何使用gulp4开发项目脚手架。

效果预览

实现思路

我将按照上文中的设计思路的步骤一步步实现,首先我们先定义一个es5类:

其中init是用来初始化用的,参数el代表需要挂载的元素或元素id,option为传入的可选项,稍后会在代码中体现,通常这也是面向对象的常用套路。

  1. 绘制画布

Gcode.prototype = {	
    constructor: Gcode,	
    init: function() {	
        if(this.el.getContext) {	
            isSupportCanvas = true;	
            var ctx = this.el.getContext('2d'),	
            // 设置画布宽高	
            cw = this.el.width = this.option.width || 200,	
            ch = this.el.height = this.option.height || 40;	
        }	
    }	
}

这里我们在初始化方法中先定义一个canvas画布,宽高为用户自定义的宽高,默认为200*40。

  1. 绘制干扰线

这里我们对类Gcode定义原型方法drawLine,然后通过for循环绘制随机位置的线条,为了让canvas每次点击能清空之前的干扰线,我们使用clearRect来清除画布。

  1. 生成随机不重复的n个字符

我们通过递归实现,如下==:

// 生成唯一文字	
generateUniqueText: function(source, hasList, limit) {	
    var text = source[Math.floor(Math.random()*limit)];	
    if(hasList.indexOf(text) > -1) {	
        return this.generateUniqueText(source, hasList, limit)	
    }else {	
        return text	
    }	
}	
// 生成指定个数的随机文字	
randomText: function(len) {	
    var source = ['a', 'b', 'c', 'd', 'e',	
    'f', 'g', 'h', 'i', 'j',	
    'k', 'l', 'm', 'o', 'p',	
    'q', 'r', 's', 't', 'u',	
    'v', 'w', 'x', 'y', 'z'];	
    var result = [];	
    var sourceLen = source.length;	
    for(var i=0; i< len; i++) {	
        var text = this.generateUniqueText(source, result, sourceLen);	
        result.push(text)	
    }	
    return result.join('')	
}

我们通过定义一个字母表,传入生成的随机字母的个数,配合generateUniqueText来实现生成唯一不重复的n个随机字符。当然笔者认为这个方法并不优雅,你也可以使用uuid的方式或者更好的方式,欢迎随时和笔者交流。

  1. 用canvas绘制文字

这里和上文画线实现类似。就不做过多介绍了。

  1. 初始化和canvas点击事件

接下来我们看看完整的初始化代码:

点击事件主要是为了用户点击可以切换验证码:

到此,一个完整的验证码组件实现完成,怎么用呢?如下:

new Gcode('#canvas_code', {	
        lineNum: 6,  // 可选	
        textLen: 4,  // 可选	
        width: 200,  // 可选	
        height: 50   // 可选	
    })

完整代码如下,欢迎学习交流:

// canvas绘制图形验证码	
    (function(){	
        function Gcode(el, option) {	
            this.el = typeof el === 'string' ? document.querySelector(el) : el;	
            this.option = option;	
            this.init();	
        }	
        Gcode.prototype = {	
            constructor: Gcode,	
            init: function() {	
                if(this.el.getContext) {	
                    isSupportCanvas = true;	
                    var ctx = this.el.getContext('2d'),	
                    // 设置画布宽高	
                    cw = this.el.width = this.option.width || 200,	
                    ch = this.el.height = this.option.height || 40,	
                    textLen = this.option.textLen || 4,	
                    lineNum = this.option.lineNum || 4;	
                    var text = this.randomText(textLen);	
        	
                    this.onClick(ctx, textLen, lineNum, cw, ch);	
                    this.drawLine(ctx, lineNum, cw, ch);	
                    this.drawText(ctx, text, ch);	
                }	
            },	
            onClick: function(ctx, textLen, lineNum, cw, ch) {	
                var _ = this;	
                this.el.addEventListener('click', function(){	
                    text = _.randomText(textLen);	
                    _.drawLine(ctx, lineNum, cw, ch);	
                    _.drawText(ctx, text, ch);	
                }, false)	
            },	
            // 画干扰线	
            drawLine: function(ctx, lineNum, maxW, maxH) {	
                ctx.clearRect(0, 0, maxW, maxH);	
                for(var i=0; i < lineNum; i++) {	
                    var dx1 = Math.random()* maxW,	
                        dy1 = Math.random()* maxH,	
                        dx2 = Math.random()* maxW,	
                        dy2 = Math.random()* maxH;	
                    ctx.strokeStyle = 'rgb(' + 255*Math.random() + ',' + 255*Math.random() + ',' + 255*Math.random() + ')';	
                    ctx.beginPath();	
                    ctx.moveTo(dx1, dy1);	
                    ctx.lineTo(dx2, dy2);	
                    ctx.stroke();	
                }	
            },	
            // 画文字	
            drawText: function(ctx, text, maxH) {	
                var len = text.length;	
                for(var i=0; i < len; i++) {	
                    var dx = 30 * Math.random() + 30* i,	
                        dy = Math.random()* 5 + maxH/2;	
                    ctx.fillStyle = 'rgb(' + 255*Math.random() + ',' + 255*Math.random() + ',' + 255*Math.random() + ')';	
                    ctx.font = '30px Helvetica';	
                    ctx.textBaseline = 'middle';	
                    ctx.fillText(text[i], dx, dy);	
                }	
            },	
            // 生成指定个数的随机文字	
            randomText: function(len) {	
                var source = ['a', 'b', 'c', 'd', 'e',	
                'f', 'g', 'h', 'i', 'j',	
                'k', 'l', 'm', 'o', 'p',	
                'q', 'r', 's', 't', 'u',	
                'v', 'w', 'x', 'y', 'z'];	
                var result = [];	
                var sourceLen = source.length;	
                for(var i=0; i< len; i++) {	
                    var text = this.generateUniqueText(source, result, sourceLen);	
                    result.push(text)	
                }	
                return result.join('')	
            },	
            // 生成唯一文字	
            generateUniqueText: function(source, hasList, limit) {	
                var text = source[Math.floor(Math.random()*limit)];	
                if(hasList.indexOf(text) > -1) {	
                    return this.generateUniqueText(source, hasList, limit)	
                }else {	
                    return text	
                }	
            }	
        }	
        new Gcode('#canvas_code', {	
            lineNum: 6	
        })	
    })();

如果想体验实际案例效果和技术交流,或者感受更多原创canvas,h5游戏demo,可以关注下方公众号体验哦

更多推荐

欢迎关注下方公众号,获取更多前端知识精粹和学习社群:

在公众号点击进群,可以加入vue学习小组,一起学习前端技术;

回复 学习路径,将获取笔者多年从业经验的前端学习路径的思维导图

趣谈前端

Vue、React、小程序、Node 

 

前端 算法|性能|架构|安全