Canvas入門實(shí)戰(zhàn)之實(shí)現(xiàn)一個(gè)圖形驗(yàn)證碼
本文主要介紹用canvas實(shí)現(xiàn)圖形驗(yàn)證碼的一些思路以及如何用javascript面向?qū)ο蟮姆绞礁押玫膶?shí)現(xiàn)canvas的功能,關(guān)于canvas的一些基本使用方法和API我整理了一個(gè)思維導(dǎo)圖,大家感興趣的可以參考學(xué)習(xí)。

你將收獲
- 閉包的使用
- canvas常用api的使用
- javascript面向?qū)ο蟮膶?shí)現(xiàn)方式
- 實(shí)現(xiàn)一個(gè)canvas的圖形驗(yàn)證碼的一般思路和常用算法
設(shè)計(jì)思路
- 用canvas生成畫(huà)布
- 用canvas畫(huà)干擾線或躁點(diǎn)
- 生成隨機(jī)不重復(fù)的n的字母
- 用canvas繪制文字
- 初始化和canvas點(diǎn)擊事件
- 組件化封裝
文末將附上組件封裝的源碼,歡迎大家隨時(shí)溝通交流。關(guān)于項(xiàng)目的打包,我將使用自己基于gulp4搭建的9012教你如何使用gulp4開(kāi)發(fā)項(xiàng)目腳手架。
效果預(yù)覽

實(shí)現(xiàn)思路
我將按照上文中的設(shè)計(jì)思路的步驟一步步實(shí)現(xiàn),首先我們先定義一個(gè)es5類:
- function Gcode(el, option) {
- this.el = typeof el === 'string' ? document.querySelector(el) : el;
- this.option = option;
- this.init();
- }
其中init是用來(lái)初始化用的,參數(shù)el代表需要掛載的元素或元素id,option為傳入的可選項(xiàng),稍后會(huì)在代碼中體現(xiàn),通常這也是面向?qū)ο蟮某S锰茁贰?/p>
1.繪制畫(huà)布
- Gcode.prototype = {
- constructor: Gcode,
- init: function() {
- if(this.el.getContext) {
- isSupportCanvas = true;
- var ctx = this.el.getContext('2d'),
- // 設(shè)置畫(huà)布寬高
- cw = this.el.width = this.option.width || 200,
- ch = this.el.height = this.option.height || 40;
- }
- }
- }
這里我們?cè)诔跏蓟椒ㄖ邢榷x一個(gè)canvas畫(huà)布,寬高為用戶自定義的寬高,默認(rèn)為200*40。
2.繪制干擾線
- // 畫(huà)干擾線
- 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();
- }
- }
這里我們對(duì)類Gcode定義原型方法drawLine,然后通過(guò)for循環(huán)繪制隨機(jī)位置的線條,為了讓canvas每次點(diǎn)擊能清空之前的干擾線,我們使用clearRect來(lái)清除畫(huà)布。
3.生成隨機(jī)不重復(fù)的n個(gè)字符
我們通過(guò)遞歸實(shí)現(xià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
- }
- }
- // 生成指定個(gè)數(shù)的隨機(jī)文字
- 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('')
- }
我們通過(guò)定義一個(gè)字母表,傳入生成的隨機(jī)字母的個(gè)數(shù),配合generateUniqueText來(lái)實(shí)現(xiàn)生成唯一不重復(fù)的n個(gè)隨機(jī)字符。當(dāng)然筆者認(rèn)為這個(gè)方法并不優(yōu)雅,你也可以使用uuid的方式或者更好的方式,歡迎隨時(shí)和筆者交流。
4.用canvas繪制文字
- // 畫(huà)文字
- 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);
- }
- },
這里和上文畫(huà)線實(shí)現(xiàn)類似。就不做過(guò)多介紹了。
5.初始化和canvas點(diǎn)擊事件
接下來(lái)我們看看完整的初始化代碼:
- init: function() {
- if(this.el.getContext) {
- isSupportCanvas = true;
- var ctx = this.el.getContext('2d'),
- // 設(shè)置畫(huà)布寬高
- 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);
- }
- }
點(diǎn)擊事件主要是為了用戶點(diǎn)擊可以切換驗(yàn)證碼:
- 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)
- }
到此,一個(gè)完整的驗(yàn)證碼組件實(shí)現(xiàn)完成,怎么用呢?如下:
- new Gcode('#canvas_code', {
- lineNum: 6, // 可選
- textLen: 4, // 可選
- width: 200, // 可選
- height: 50 // 可選
- })
完整代碼如下,歡迎學(xué)習(xí)交流:
- // canvas繪制圖形驗(yàn)證碼
- (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'),
- // 設(shè)置畫(huà)布寬高
- 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)
- },
- // 畫(huà)干擾線
- 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();
- }
- },
- // 畫(huà)文字
- 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);
- }
- },
- // 生成指定個(gè)數(shù)的隨機(jī)文字
- 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
- })
- })();