💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# canvas ## 1.简介 HTML5 的新特性:在添加完成 `canvas` 元素后,通过 `js` 脚本绘制图案。 ## 2.验证码案例 ![](https://img.kancloud.cn/29/ae/29aee7f3402b868d250b5f8d0c953f42_472x513.png) 案例说明:在用户登录时提供验证码功能(登录错误超过几次之后显示,设置验证码的实效时间) 案例环境:vue 参考文档:[登录随机验证码的实现](https://www.cnblogs.com/moning/p/7868731.html)、[如何给localStorage设置一个过期时间?](https://blog.csdn.net/weixin_43254766/article/details/83618630) ### 2.1实现思路: 使用canvas绘制验证码 localstorage中存储密码错误次数及第一次密码错误时间 账号或密码验证错误时从缓存中读取错误次数 ### 2.2具体代码 验证码页面元素canvas属性方法设置 ~~~ <li class="verification" v-if="showVerify">          <input @blur="handleVerify" type="text" v-model.trim="verification" placeholder="请输入验证码" />          <Icon title="刷新验证码" @click="drawVerification" class="icon-refresh" type="md-refresh" />          <canvas title="点击刷新" @click.prevent="drawVerification" id="canvas" width="150px" height="40px"></canvas>        </li> ~~~ 绘制验证码方法 ~~~ // 绘制验证码方法 drawVerification() {  const canvas = document.getElementById("canvas");  const str =        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";  let verifyVal = ""; // 初始定义随机数  const num = 4; // 设置随机数个数  const width = canvas.clientWidth;  const height = canvas.clientHeight;  const ctx = canvas.getContext("2d");  // 设置、填充画布底板  ctx.fillStyle = randomColor(180, 240);  ctx.fillRect(0, 0, width, height);  // 绘制随机验证码  for (let i = 0; i < num; i++) {    const x = (width / num) * i + 15;    const y = randomNum(height / 1.8, height / 1.2);    const deg = randomNum(-45, 45);    const txt = str[randomNum(0, str.length)];    verifyVal += txt; // 获取一个随机数    ctx.fillStyle = randomColor(10, 100); // 字体填充随机颜色    ctx.font = randomNum(18, 25) + "px SimHei"; // 设置字体    ctx.translate(x, y); // 将当前xy坐标作为原始坐标    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 < num; i++) {    // 定义画笔颜色    ctx.strokeStyle = randomColor(90, 180);    ctx.beginPath();    // 定义线条路径    ctx.moveTo(randomNum(0, width), randomNum(0, height));    ctx.lineTo(randomNum(0, width), randomNum(0, height));    // 按路径绘制    ctx.stroke(); }  // 绘制圆点  for (let i = 0; i < num * 10; i++) {    ctx.fillStyle = randomColor(0, 255);    ctx.beginPath();    ctx.arc(randomNum(0, width), randomNum(0, height), 1, 0, 2 * Math.PI);    ctx.fill(); }  this.verifyVal = verifyVal; } ~~~ 登录验证 ~~~ async login() {  // ...  try {    const res = await loginCheck(userInfo);    // ...    localStorageUtils.remove("failureTimes");    localStorageUtils.remove("failureTimes__expired__"); } catch (err) {    const failureTimes = (localStorageUtils.get("failureTimes") || 0) + 1;    // 登录验证错误超过4次时,显示并绘制    if (failureTimes > 4) {   this.showVerify = true;   if (this.verification) {   this.verification = "";   }   await this.$nextTick();   this.drawVerification();   }    if (failureTimes === 1) {   const userInfo = localStorageUtils.get("userInfo");   if (userInfo && userInfo.password) {        // 若选择了记住密码,首次登录验证不通过时将密码清空        localStorageUtils.save("userInfo", {          loginName: userInfo.loginName       });     }   }    // 缓存密码错误次数与当前错误时间    localStorageUtils.save("failureTimes", failureTimes, true); } } ~~~ 缓存控制 ~~~ export default {  save(key, value, expired = false) {    this.remove(key);    if (expired) {      localStorage.setItem(        `${key}__expired__`,        Date.now() + 1000 * 60 * 60 * 3     );   }    localStorage.setItem(key, JSON.stringify(value)); },  get(key) {    const expired = localStorage.getItem(`${key}__expired__`);    if (expired && Date.now() >= expired) {      // 超出时间期限      this.remove(key);      this.remove(`${key}__expired__`);      return;   }    const value = localStorage.getItem(key);    if (value) {      try {        return value ? JSON.parse(value) : null;     } catch (error) {        // JSON.parse("xxx") 非对象字符串出错,直接返回对应的字符串        return value ? value : null;     }   } else {      return;   } },  remove(key) {    localStorage.removeItem(key); },  removeAll() {    localStorage.clear(); } }; ~~~