antd pro 验证码(hook)

1,264 阅读1分钟

设计思路与之前的相同,不过为了解耦和优化DOM操作,我们分解了改组件,使用hook来完成。

代码如下:

import { useState, useEffect, useRef } from 'react';
function PicValidate(props) {
    const {
        width,
        height,
} = props;
const initialOptions = {
      // actually invalid in development but maybe useful in future
    canvasId: 'verifyCanvas',
    width: '250',
    height: '100',
    // blend (数字字母混合)
    // number:(纯数字)
    // letter 纯字母
    type: 'blend',
     code: '',
  };
  // no need to save data in states
  // const [initialOptions, setOptions] = useState(initialOption);
  if (Object.prototype.toString.call(props) === '[object Object]'){
    for(let i in props) {
      initialOptions[i] = props[i];
    }
  }
  initialOptions.numArr = [0,1,2,3,4,5,6,7,8,9];
  initialOptions.letterArr = getAllLetters();
  const canvasItem = useRef();
  useEffect(() => {
    refreshIt(initialOptions, canvasItem);
  },[width])
  return (
    <canvas
      ref={canvasItem}
      width={width}
      height={height}
      style={{cursor: 'pointer'}}
      onClick={() => {
        refreshIt(initialOptions, canvasItem)
      }}
    >您的浏览器版本不支持使用canvas</canvas>
  );}
const refreshIt = (options, canvasItem) => {
  options.code = '';
  let canvas = canvasItem.current;
  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();
  }
  if (options.callback) {
    options.callback(options.code.toLowerCase());
  }}
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 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 Randoms(props){
  const {
    callback,
  } = props;
  const divRef = useRef();
  useEffect(() => {
    setOffSet([divRef.current.offsetWidth, divRef.current.offsetHeight])
  }, [])
  const [offset, setOffSet] = useState([0,0])
  return (
    <div ref={divRef}>
      <PicValidate
        type="blend"
        width={offset[0] || '100'}
        height={offset[1] || '40'}
        callback={callback}
      />
    </div>
  )}
export default Randoms;

在父组件中直接调用即可:

<Randoms
  callback={(picValidateCode) => {
    this.setState({
      picValidateCode,
    })
  }}
/>

以上。