Antd_pro 表单验证码尝试

678 阅读2分钟

本次尝试的是基于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()即可。