Vue3使用纯前端实现图形验证码

前期准备

Canvas 教程 – Web API | MDN (mozilla.org)

了解Canvas元素如何使用

正式工作

创建文件

创建IdentifyCode.vue作为组件,为了显示验证码,我们首先要声明一个canvas元素

<template>
  <canvas id="code-canvas" :width="picWidth" :height="picHeight"></canvas>
</template>

要做到下图的效果,我们首先要来进行一下元素的分析

该验证码由4个数字或字母组成,大小随机,旋转角度随机,颜色随机;背景颜色随机,有随机颜色的点状随机分布,随机干扰线。

一个验证码元素还具有长度和宽度两个属性,所以在data里面进行一下声明,便于后续函数使用。

data() {
  return {
    picWidth: 120,
    picHeight: 35,
  };
},  
props: {
    code: {
    type: String,
    default: "2244",
 },
},

验证码充满了随机性,于是我们开始编写随机函数。

getRandomVal(min, max) {
  return Math.floor(Math.random() * (max - min) + min);
}
getRandomCol(min, max) {
  var r = this.getRandomVal(min, max);
  var g = this.getRandomVal(min, max);
  var b = this.getRandomVal(min, max);
  return "rgb(" + r + "," + g + "," + b + ")";
}

这里随机颜色返回的是rgb(255,255,255)这种格式的字符串。完成了随机生成数字和颜色后,接下来分步进行生成验证码的函数。

进行背景颜色的随机生成

drawBackground(context) {
  context.fillStyle = this.getRandomCol(180, 240);
  context.fillRect(0, 0, this.picWidth, this.picHeight);
}

进行随机文本的填充

drawText(context) {
  for (let i = 0; i < this.code.length; i++) {
    context.fillStyle = this.getRandomCol(0, 180);
    context.font = this.getRandomVal(20, 40) + "px serif";
    let deg = this.getRandomVal(-20, 20);
    let x = 20 * (i + 1);
    let y = this.getRandomVal(25, 32);
    context.translate(x, y);
    context.rotate((Math.PI / 180) * deg);
    context.fillText(this.code[i], 0, 0);
    context.rotate(-(Math.PI / 180) * deg);
    context.translate(-x, -y);
  }
},

在这里要注意,是先对画布进行平移操作再进行旋转,因为画布旋转后再平移,会让画布以新的旋转后的参考系进行平移,在填充文本之后要注意恢复画布到正常的状态,为下一步生成文本做准备。

进行随机干扰点的生成

drawDot(context) {
  for (let i = 0; i < 20; i++) {
    context.fillStyle = this.getRandomCol(0, 255);
    context.beginPath();
    context.arc(
      this.getRandomVal(0, this.picWidth),
      this.getRandomVal(0, this.picHeight),
      1,
      0,
      Math.PI * 2
    );
    context.fill();
  }
},
arc(x, y, radius, startAngle, endAngle, anticlockwise)
//画一个以(x,y)为圆心的以 radius 为半径的圆弧(圆),从 startAngle 开始到 endAngle 结束,按照 anticlockwise 给定的方向(默认为顺时针)来生成。

 

这里使用的是canvas的路径绘制函数,但是每一次beginPath后因为只绘制了一个圆弧,所以可以看做画了一个实心圆形。

进行随机干扰线的生成

drawLines(context) {
  for (let i = 0; i < 2; i++) {
    context.strokeStyle = this.getRandomCol(0, 155);
    context.beginPath();
    context.moveTo(
      this.getRandomVal(0, this.picWidth),
      this.getRandomVal(0, this.picHeight)
    );
    context.lineTo(
      this.getRandomVal(0, this.picWidth),
      this.getRandomVal(0, this.picHeight)
    );
    context.stroke();
  }
},

这里是通过for循环生成2条干扰线,使用moveTo和lineTo进行落笔、提笔操作。

完善组件加载出来就绘制验证码功能

在methods中添加drawPic()函数,将上述功能放在该函数中进行实现。

drawPic() {
  var canvas = document.getElementById("code-canvas");
  var context = canvas.getContext("2d");
  this.drawBackground(context);
  this.drawText(context);
  this.drawDot(context);
  this.drawLines(context);
},

在mounted时执行drawPic()。

mounted() {
  this.drawPic();
},

在父界面引入该验证码组件

声明参数code作为生成随机验证码字符串的中介

data() {
  return {
    code: ref(""),
  };
},

在methods中添加生成随机验证码文本的函数

genCode() {
  this.code = "";
  var str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  for(let i = 0;i < 4;i++){
    let index = Math.floor(Math.random()*62);
    this.code += str[index];
  }
},

在template中添加该组件并传入code,为组件添加点击事件实现点击刷新

<IdentifyCode :code="code" @click="genCode()"></IdentifyCode>

要实现页面加载出来就随机生成一个随机验证码字符串,我们还需要为生命周期mounted添加genCode()

mounted(){
  this.genCode();
}

这个时候我们会发现,验证码中并没有文本生成,其原因是在mounted之后code才生成,而该组件的code早在这个阶段之前就传入并且在组件的mounted中进行了一次drawPic(),导致后续code更新时,该验证码不会发生变化,所以我们需要在组件中添加监听事件,监听code是否发生改变,若改变则再次执行drawPic()。

watch: {
  code() {
    this.drawPic();
  },
},

组件代码一览

<template>
  <canvas id="code-canvas" :width="picWidth" :height="picHeight"></canvas>
</template>

<script>
export default {
  name: "IdentifyCode",
  props: {
    code: {
      type: String,
      default: "2244",
    },
  },
  data() {
    return {
      picWidth: 120,
      picHeight: 35,
    };
  },
  methods: {
    getRandomVal(min, max) {
      return Math.floor(Math.random() * (max - min) + min);
    },
    getRandomCol(min, max) {
      var r = this.getRandomVal(min, max);
      var g = this.getRandomVal(min, max);
      var b = this.getRandomVal(min, max);
      return "rgb(" + r + "," + g + "," + b + ")";
    },
    drawPic() {
      var canvas = document.getElementById("code-canvas");
      var context = canvas.getContext("2d");
      this.drawBackground(context);
      this.drawText(context);
      this.drawDot(context);
      this.drawLines(context);
    },
    drawBackground(context) {
      context.fillStyle = this.getRandomCol(180, 240);
      context.fillRect(0, 0, this.picWidth, this.picHeight);
    },
    drawText(context) {
      for (let i = 0; i < this.code.length; i++) {
        context.fillStyle = this.getRandomCol(0, 180);
        context.font = this.getRandomVal(20, 40) + "px serif";
        let deg = this.getRandomVal(-20, 20);
        let x = 20 * (i + 1);
        let y = this.getRandomVal(25, 32);
        context.translate(x, y);
        context.rotate((Math.PI / 180) * deg);
        context.fillText(this.code[i], 0, 0);
        context.rotate(-(Math.PI / 180) * deg);
        context.translate(-x, -y);
      }
    },
    drawDot(context) {
      for (let i = 0; i < 20; i++) {
        context.fillStyle = this.getRandomCol(0, 255);
        context.beginPath();
        context.arc(
          this.getRandomVal(0, this.picWidth),
          this.getRandomVal(0, this.picHeight),
          1,
          0,
          Math.PI * 2
        );
        context.fill();
      }
    },
    drawLines(context) {
      for (let i = 0; i < 2; i++) {
        context.strokeStyle = this.getRandomCol(0, 155);
        context.beginPath();
        context.moveTo(
          this.getRandomVal(0, this.picWidth),
          this.getRandomVal(0, this.picHeight)
        );
        context.lineTo(
          this.getRandomVal(0, this.picWidth),
          this.getRandomVal(0, this.picHeight)
        );
        context.stroke();
      }
    },
  },
  watch: {
    code() {
      this.drawPic();
    },
  },
  mounted() {
    this.drawPic();
  },
};
</script>

 

 

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇