本次尝试的是基于canvas的验证码生成方式,生成的验证码表现形式为类图片的canvas元素。
需求明确,我们直接启动。在这里感谢@张浩楠的前端随机验证码一文。
采用函数式组件的方式 我们写一个基础的组件。
import React, { useState, useEffect } from 'antd';
export default function PicValidate(container){
}
我们先初始化一下默认的canvas数据:
const initialOption = { // 容器ID id: '', // canvas Id canvasId: 'verifyCanvas', width: '100', height: '100', // blend (数字字母混合) // number:(纯数字) // letter 纯字母 type: 'blend', code: '', };
将container的值赋值给它,为防止万一 我们做一下类型判断:
if (Object.prototype.toString.call(container) === '[object Object]'){ for(let i in container) { initialOptions[i] = container[i]; } }
设定验证码的范围:
initialOptions.numArr = [0,1,2,3,4,5,6,7,8,9];initialOptions.letterArr = getAllLetters();
const getAllLetters = function(){ let letterStr = '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'; return letterStr.split(',');}
开始绘图。
const init = (options) => { // 绘图 let con = document.getElementById(options.id); // 创建绘图模块 let canvas = document.createElement('canvas'); options.width = con && con.offsetWidth > 0 ? con.offsetWidth : '100'; options.height = con && con.offsetHeight > 0 ? con.offsetHeight : '100'; canvas.id = options.canvasId; canvas.width = options.width; canvas.height = options.height; canvas.style.cursor = 'pointer'; canvas.innerHTML = '您的浏览器版本不支持使用canvas';
// 这边我想了一下 这下面这一段可能不是必要的(我觉得可以直接简单的写成 if(con) con.appendChild(canvas)) if (con) { let nodeListArray = Array.from(con.childNodes) || []; if (con.childNodes && !nodeListArray.some(item => item.tagName === 'CANVAS')) { con.appendChild(canvas); } } refreshIt(options); //console.log('again') canvas.onclick = function () { refreshIt(options); } }
画完了底部我们准备绘制内容。
const refreshIt = (options) => { options.code = ''; let canvas = document.getElementById(options.canvasId || options); let ctx; let txtArr; if (canvas && canvas.getContext) { ctx = canvas.getContext('2d'); } else { return ; } ctx.textBaseline = 'middle'; // 样式设置 ctx.fillStyle = randomColor(180,240); ctx.fillRect(0,0,options.width,options.height); // 设置验证码对应数组范围 range if (options.type === 'blend') { txtArr = options.numArr.concat(options.letterArr); } else if (options.type === 'number') { txtArr = options.numArr; } else { txtArr = options.letterArr; } // 绘制页面主要内容及整体样式 for (let i = 0; i < 4; i++) { let txt = txtArr[randomNum(0,txtArr.length)]; options.code += txt; ctx.font = randomNum(options.height / 2, options.height) + 'px SimHei'; ctx.fillStyle = randomColor(50,160); // 随机生成字体颜色 ctx.shadowOffsetX = randomNum(-2,2); ctx.shadowOffsetY = randomNum(-2,2); ctx.shadowBlur = randomNum(-2,2); ctx.shadowColor = 'rgba(0,0,0,0.3)'; let x = options.width /6 * (i+1); let y = options.height / 2; let deg = randomNum(-30, 30); // 设置旋转的角度及坐标原点 ctx.translate(x,y); ctx.rotate(deg*Math.PI / 180); ctx.fillText(txt, 0, 0); // 恢复旋转角度和坐标原点 ctx.rotate(-deg * Math.PI / 180); ctx.translate(-x, -y); } // 绘制干扰线 for (let i = 0; i < 4; i++){ ctx.strokeStyle = randomColor(40,180); ctx.beginPath(); ctx.moveTo(randomNum(0,options.width), randomNum(0,options.height)); ctx.lineTo(randomNum(0,options.width), randomNum(0,options.height)); ctx.stroke(); } // 绘制干扰点 for (let i = 0; i <options.width / 4; i++){ ctx.fillStyle = randomColor(0,255); ctx.beginPath(); ctx.arc(randomNum(0, options.width), randomNum(0, options.height), 1, 0, 2*Math.PI); ctx.fill(); } }
附上两个随机的函数:
const randomNum = (min, max) => { return Math.floor(Math.random()*(max - min) + min); } const randomColor = (min, max) => { let r = randomNum(min, max); let g = randomNum(min, max); let b = randomNum(min, max); return `rgb(${r},${g},${b})` }
到这里我们就已经完成绘图了?不, 还差一步:
function PicValidate(container) {
//...
useEffect(() => { init(initialOptions) }, []);}
在父组件调用的时候 我们只需要这样显式传值即可:
<div id="picValidateArea" className={styles.picValidateArea}> <PicValidate id="picValidateArea" width={120} callback = {(picValidateCode) => { this.setState({ picValidateCode, }) }} /></div>
展示一下最后的画面:
以上。
附: 在父组件中如果需要刷新验证码图片可以直接 const canvas = document.getElementById('verifyCanvas')之后 canvas.onclick()即可。