Angular 纯前端实现图形验证码

2,178 阅读1分钟

效果图

登陆时,需要添加双因子验证,需求添加一个验证码,纯前端实现输入刷新校验。

废话不多说,直接上代码!

TS代码:

import { Component, ElementRef, ViewChild, OnInit } from '@angular/core';

@Component({
  selector: 'app-verify-code',
  templateUrl: './verify-code.component.html',
})
export class VerifyCodeComponent implements OnInit {
  @ViewChild('verifyCanvas') verifyCanvas: ElementRef;

  codeLength = 4; // 设置验证码长度
  options = {
    // 默认options参数值
    id: 'v_container', // 容器Id
    canvasId: 'verifyCanvas', // canvas的ID
    width: 100, // 默认canvas宽度
    height: 40, // 默认canvas高度
    type: 'blend', // 图形验证码默认类型blend:数字字母混合类型、number:纯数字、letter:纯字母
    code: '',
    numArr: [],
    letterArr: [],
  };
  ctx;
  constructor() {}

  ngOnInit() {
    this.ctx = this.verifyCanvas.nativeElement.getContext('2d');

    this.options.numArr = '0,1,2,3,4,5,6,7,8,9'.split(',');
    this.options.letterArr = this.getAllLetter();
    this.refresh();
  }

  // 生成验证码
  refresh() {
    this.options.code = '';
    this.ctx.textBaseline = 'middle';

    this.ctx.fillStyle = this.randomColor(180, 240);
    this.ctx.fillRect(0, 0, this.options.width, this.options.height);

    let txtArr = [];
    if (this.options.type === 'blend') {
      // 判断验证码类型
      txtArr = this.options.numArr.concat(this.options.letterArr);
    } else if (this.options.type === 'number') {
      txtArr = this.options.numArr;
    } else {
      txtArr = this.options.letterArr;
    }

    for (let i = 1; i <= this.codeLength; i++) {
      const txt = txtArr[this.randomNum(0, txtArr.length)];
      this.options.code += txt;
      this.ctx.font = this.randomNum(this.options.height / 2, this.options.height) + 'px SimHei'; // 随机生成字体大小
      this.ctx.fillStyle = this.randomColor(50, 160); // 随机生成字体颜色
      this.ctx.shadowOffsetX = this.randomNum(-3, 3);
      this.ctx.shadowOffsetY = this.randomNum(-3, 3);
      this.ctx.shadowBlur = this.randomNum(-3, 3);
      this.ctx.shadowColor = 'rgba(0, 0, 0, 0.3)';
      const x = (this.options.width / (this.codeLength + 1)) * i;
      const y = this.options.height / 2;
      const deg = this.randomNum(-30, 30);
      // 设置旋转角度和坐标原点
      this.ctx.translate(x, y);
      this.ctx.rotate((deg * Math.PI) / 180);
      this.ctx.fillText(txt, 0, 0);
      // 恢复旋转角度和坐标原点
      this.ctx.rotate((-deg * Math.PI) / 180);
      this.ctx.translate(-x, -y);
    }
    // 绘制干扰线
    for (let i = 0; i < 2; i++) {
      this.ctx.strokeStyle = this.randomColor(40, 180);
      this.ctx.beginPath();
      this.ctx.moveTo(this.randomNum(0, this.options.width), this.randomNum(0, this.options.height));
      this.ctx.lineTo(this.randomNum(0, this.options.width), this.randomNum(0, this.options.height));
      this.ctx.stroke();
    }
    // 绘制干扰点
    for (let i = 0; i < this.options.width / 2; i++) {
      this.ctx.fillStyle = this.randomColor(0, 255);
      this.ctx.beginPath();
      this.ctx.arc(this.randomNum(0, this.options.width), this.randomNum(0, this.options.height), 1, 0, 2 * Math.PI);
      this.ctx.fill();
    }
  }

  // 验证验证码
  validate(code) {
    const code1 = code.toLowerCase();
    const v_code = this.options.code.toLowerCase();
    // console.log(code1 + ' ---- ' + v_code);

    if (code1 === v_code) {
      return true;
    } else {
      this.refresh();
      return false;
    }
  }

  // 生成字母数组
  getAllLetter() {
    const 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(',');
  }

  // 生成一个随机数
  randomNum(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
  }

  // 生成一个随机色
  randomColor(min, max) {
    const r = this.randomNum(min, max);
    const g = this.randomNum(min, max);
    const b = this.randomNum(min, max);
    return 'rgb(' + r + ',' + g + ',' + b + ')';
  }
}

HTML代码:

<div id="v_container" (click)="refresh()" style="width: 100px;">
  <canvas #verifyCanvas width="100" height="40" style="cursor: pointer;"></canvas>
  <a (click)="refresh()" style="margin-left: 25px;">看不清?</a>
</div>