手写一个图形验证码

文章目录

    • 需求
    • 分析

需求

使用 JS 写一个验证码,并在前端进行校验
在这里插入图片描述

分析

新建文件 VueImageVerify.vue

<template><div class="img-verify"><canvas ref="verify" :width="state.width" :height="state.height" @click="handleDraw"></canvas></div>
</template><script setup>
import { reactive, onMounted, ref } from 'vue'
const verify = ref(null)
const state = reactive({pool: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890', // 字符串width: 120,height: 40,imgCode: ''
})
defineExpose({ state })
onMounted(() => {// 初始化绘制图片验证码state.imgCode = draw()
})// 点击图片重新绘制
const handleDraw = () => {state.imgCode = draw()
}// 随机数
const randomNum = (min, max) => {return parseInt(Math.random() * (max - min) + min)
}
// 随机颜色
const randomColor = (min, max) => {const r = randomNum(min, max)const g = randomNum(min, max)const b = randomNum(min, max)return `rgb(${r},${g},${b})`
}// 绘制图片
const draw = () => {// 3.填充背景颜色,背景颜色要浅一点const ctx = verify.value.getContext('2d')// 填充颜色ctx.fillStyle = randomColor(180, 230)// 填充的位置ctx.fillRect(0, 0, state.width, state.height)// 定义paramTextlet imgCode = ''// 4.随机产生字符串,并且随机旋转for (let i = 0; i < 4; i++) {// 随机的四个字const text = state.pool[randomNum(0, state.pool.length)]imgCode += text// 随机的字体大小const fontSize = randomNum(18, 40)// 字体随机的旋转角度const deg = randomNum(-30, 30)/** 绘制文字并让四个文字在不同的位置显示的思路 :* 1、定义字体* 2、定义对齐方式* 3、填充不同的颜色* 4、保存当前的状态(以防止以上的状态受影响)* 5、平移translate()* 6、旋转 rotate()* 7、填充文字* 8、restore出栈* */ctx.font = fontSize + 'px Simhei'ctx.textBaseline = 'top'ctx.fillStyle = randomColor(80, 150)/** save() 方法把当前状态的一份拷贝压入到一个保存图像状态的栈中。* 这就允许您临时地改变图像状态,* 然后,通过调用 restore() 来恢复以前的值。* save是入栈,restore是出栈。* 用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。 restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。** */ctx.save()ctx.translate(30 * i + 15, 15)ctx.rotate((deg * Math.PI) / 180)// fillText() 方法在画布上绘制填色的文本。文本的默认颜色是黑色。// 请使用 font 属性来定义字体和字号,并使用 fillStyle 属性以另一种颜色/渐变来渲染文本。// context.fillText(text,x,y,maxWidth);ctx.fillText(text, -15 + 5, -15)ctx.restore()}// 5.随机产生5条干扰线,干扰线的颜色要浅一点for (let i = 0; i < 5; i++) {ctx.beginPath()ctx.moveTo(randomNum(0, state.width), randomNum(0, state.height))ctx.lineTo(randomNum(0, state.width), randomNum(0, state.height))ctx.strokeStyle = randomColor(180, 230)ctx.closePath()ctx.stroke()}// 6.随机产生40个干扰的小点for (let i = 0; i < 40; i++) {ctx.beginPath()ctx.arc(randomNum(0, state.width), randomNum(0, state.height), 1, 0, 2 * Math.PI)ctx.closePath()ctx.fillStyle = randomColor(150, 200)ctx.fill()}return imgCode
}
</script>
<style>
.img-verify canvas {cursor: pointer;
}
</style>
  • 文件 Login.vue 中进行引入
<template><div class="login"><s-header :name="type == 'login' ? '登录' : '注册'" :back="'/home'"></s-header><img class="logo" src="https://s.yezgea02.com/1604045825972/newbee-mall-vue3-app-logo.png" alt=""><div v-if="state.type == 'login'" class="login-body login"><van-form @submit="onSubmit"><van-fieldv-model="state.username"name="username"label="用户名"placeholder="用户名":rules="[{ required: true, message: '请填写用户名' }]"/><van-fieldv-model="state.password"type="password"name="password"label="密码"placeholder="密码":rules="[{ required: true, message: '请填写密码' }]"/><van-fieldcenterclearablelabel="验证码"placeholder="输入验证码"v-model="state.verify"><template #button><vue-img-verify ref="verifyRef" /></template></van-field><div style="margin: 16px;"><div class="link-register" @click="toggle('register')">立即注册</div><van-button round block color="#1baeae" native-type="submit">登录</van-button></div></van-form></div><div v-else class="login-body register"><van-form @submit="onSubmit"><van-fieldv-model="state.username1"name="username1"label="用户名"placeholder="用户名":rules="[{ required: true, message: '请填写用户名' }]"/><van-fieldv-model="state.password1"type="password"name="password1"label="密码"placeholder="密码":rules="[{ required: true, message: '请填写密码' }]"/><van-fieldcenterclearablelabel="验证码"placeholder="输入验证码"v-model="state.verify"><template #button><vue-img-verify ref="verifyRef" /></template></van-field><div style="margin: 16px;"><div class="link-login" @click="toggle('login')">已有登录账号</div><van-button round block color="#1baeae" native-type="submit">注册</van-button></div></van-form></div></div>
</template><script setup>
import { reactive, ref } from 'vue'
import sHeader from '@/components/SimpleHeader.vue'
import vueImgVerify from '@/components/VueImageVerify.vue'
import { login, register } from '@/service/user'
import { setLocal } from '@/common/js/utils'
import md5 from 'js-md5'
import { showSuccessToast, showFailToast } from 'vant'
const verifyRef = ref(null)
const state = reactive({username: '',password: '',username1: '',password1: '',type: 'login',imgCode: '',verify: ''
})// 切换登录和注册两种模式
const toggle = (v) => {state.type = vstate.verify = ''
}// 提交登录或注册表单
const onSubmit = async (values) => {state.imgCode = verifyRef.value.state.imgCode || ''if (state.verify.toLowerCase() != state.imgCode.toLowerCase()) {showFailToast('验证码有误')return}if (state.type == 'login') {const { data } = await login({"loginName": values.username,"passwordMd5": md5(values.password)})setLocal('token', data)// 需要刷新页面,否则 axios.js 文件里的 token 不会被重置window.location.href = '/'} else {await register({"loginName": values.username1,"password": values.password1})showSuccessToast('注册成功')state.type = 'login'state.verify = ''}
}
</script><style lang="less">.login {.logo {width: 120px;height: 120px;display: block;margin: 80px auto 20px;}.login-body {padding: 0 20px;}.login {.link-register {font-size: 14px;margin-bottom: 20px;color: #1989fa;display: inline-block;}}.register {.link-login {font-size: 14px;margin-bottom: 20px;color: #1989fa;display: inline-block;}}.verify-bar-area {margin-top: 24px;.verify-left-bar {border-color: #1baeae;}.verify-move-block {background-color: #1baeae;color: #fff;}}.verify {>div {width: 100%;}display: flex;justify-content: center;.cerify-code-panel {margin-top: 16px;}.verify-code {width: 40%!important;float: left!important;}.verify-code-area {float: left!important;width: 54%!important;margin-left: 14px!important;.varify-input-code {width: 90px;height: 38px!important;border: 1px solid #e9e9e9;padding-left: 10px;font-size: 16px;}.verify-change-area {line-height: 44px;}}}}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/646483.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

河南嘉家购商贸有限公司获绿色积分信用认证

“实现绿色产业、打造完善的绿色产业链、走可持续发展共创共赢”。近日&#xff0c;河南嘉家购商贸有限公司获得绿色积分认证&#xff0c;确认了该企业在绿色消费积分领域的领先地位。 据了解&#xff0c;河南嘉家购商贸有限公司始终将绿色积分视为企业发展的核心要素。全面优化…

如何实现无公网ip远程访问本地websocket服务端【内网穿透】

文章目录 1. Java 服务端demo环境2. 在pom文件引入第三包封装的netty框架maven坐标3. 创建服务端,以接口模式调用,方便外部调用4. 启动服务,出现以下信息表示启动成功,暴露端口默认99995. 创建隧道映射内网端口6. 查看状态->在线隧道,复制所创建隧道的公网地址加端口号7. 以…

G1与ZGC

G1垃圾收集器(-XX:UseG1GC)详解 G1(Garbage-First)是一款面向服务器的垃圾收集器&#xff0c;主要针对配备多颗处理器及大容量内存的机器。以极高概率满足GC停顿时间要求的同时&#xff0c;还具备高吞吐量性能特性。 G1把内存区域划分为小格子(Region)&#xff0c;最多可以有2…

java常见的面试问题

目录 一、异常 1、 throw 和 throws 的区别&#xff1f; 2、 final、finally、finalize 有什么区别&#xff1f; 3、try-catch-finally 中哪个部分可以省略&#xff1f; 4、try-catch-finally 中&#xff0c;如果 catch 中 return 了&#xff0c;finally 还会执行吗&#…

大创项目推荐 题目:垃圾邮件(短信)分类 算法实现 机器学习 深度学习 开题

文章目录 1 前言2 垃圾短信/邮件 分类算法 原理2.1 常用的分类器 - 贝叶斯分类器 3 数据集介绍4 数据预处理5 特征提取6 训练分类器7 综合测试结果8 其他模型方法9 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于机器学习的垃圾邮件分类 该项目…

DP读书:在常工院的2023年度总结

DarrenPig的年度总结 这是最好的时代&#xff0c;这是最坏的时代。——狄更斯 这是最好的时代&#xff0c;这是最坏的时代。——狄更斯 这是最好的时代&#xff0c;这是最坏的时代。——狄更斯 一、2023我的感受 不就是2023吗&#xff0c;不就是一年的经历吗&#xff0c;大家…

Spring Boot 集成 API 文档 - Swagger、Knife4J、Smart-Doc

文章目录 1.OpenAPI 规范2.Swagger: 接口管理的利器3.Swagger 与 SpringFox&#xff1a;理念与实现4.Swagger 与 Knife4J&#xff1a;增强与创新5.案例&#xff1a;Spring Boot 整合 Swagger35.1 引入 Swagger3 依赖包5.2 优化路径匹配策略兼容 SpringFox5.3 配置 Swagger5.4 S…

硅像素传感器文献调研(九)3

欧洲X射线自由电子激光器抗辐射像素传感器的设计和初步试验 摘要 目前正在汉堡建造的欧洲X射线自由电子激光器的高强度和高重复率需要硅传感器&#xff0c;该传感器可以在高偏置电压下工作3年&#xff0c;承受高达1 GGy的X射线剂量。在AGIPD合作范围内&#xff0c;研究了由四家…

# [NOI2019] 斗主地 洛谷黑题题解

[NOI2019] 斗主地 题目背景 时限 4 秒 内存 512MB 题目描述 小 S 在和小 F 玩一个叫“斗地主”的游戏。 可怜的小 S 发现自己打牌并打不过小 F&#xff0c;所以他想要在洗牌环节动动手脚。 一副牌一共有 n n n 张牌&#xff0c;从上到下依次标号为 1 ∼ n 1 \sim n 1∼…

如何在Shopee平台上进行选品 :充分利用渠道获取灵感和数据支持

在Shopee平台上进行选品是一个关键的决策过程&#xff0c;它直接影响到卖家的销售业绩和店铺的发展。为了帮助卖家更好地进行选品&#xff0c;Shopee提供了多种渠道来获取灵感和数据支持。下面将介绍一些主要的选品渠道以及如何利用它们来进行选品。 先给大家推荐一款shopee知…

如何快速解决局域网IP冲突问题?连接IP地址冲突如何用代理IP搞定?

随着互联网的普及&#xff0c;局域网已经成为了许多家庭和企业中不可或缺的一部分。然而&#xff0c;局域网中的IP地址冲突问题却时常困扰着用户。一旦出现IP地址冲突&#xff0c;网络连接就会变得不稳定&#xff0c;甚至无法正常上网。那么&#xff0c;如何快速解决局域网IP冲…

[极客大挑战 2019]Upload1

直接上传php一句话木马&#xff0c;提示要上传image 把文件名改成gif并加上gif文件头后&#xff0c;绕过了对image类型的检测&#xff0c;但是提示文件内含有<?&#xff0c;且bp抓包后改回php也会被检测 那我们考虑使用js执行php代码 <script languagephp>eval($_PO…

算力、应用、方案,联想布局全栈AI,以自身制造与供应链范本助力千行百业智能化转型升级

1月23日-24日&#xff0c;联想集团举办主题为“算领AI时代 筑基智能变革”的擎智媒体沙龙和新IT思享会“走进联想”活动。在活动中&#xff0c;联想集团副总裁、中国区首席市场官王传东表示&#xff0c;今年是联想成立40周年&#xff0c;联想已构建了全栈智能布局&#xff0c;将…

WMS仓储管理系统可视化数据看板解决方案

在快节奏的现代商业环境中&#xff0c;有效的仓储管理对于确保供应链的顺畅运作至关重要。随着科技的飞速发展&#xff0c;传统的仓储管理方式已逐渐被数字化、自动化的管理方式所取代。其中&#xff0c;WMS仓储管理系统可视化数据看板作为新兴的仓储管理工具&#xff0c;为仓库…

TCP的连接和关闭的那些事

一、基础概念 1、啥是TCP&#xff1f; 它是面向连接的一种协议&#xff0c;任何数据发送之前都需要建立连接。 2、TCP/IP协议的四层中那一层&#xff1f; TCP位于运输层&#xff0c;详见下图 3、TCP协议的状态机有哪些? 在链接建立和断开不同阶段都有不同的状态&#xf…

Linux 驱动开发基础知识——编写LED驱动程序(三)

个人名片&#xff1a; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生 &#x1f42f;个人主页&#xff1a;妄北y &#x1f427;个人QQ&#xff1a;2061314755 &#x1f43b;个人邮箱&#xff1a;2061314755qq.com &#x1f989;个人WeChat&#xff1a;V…

边裁边招,2024大厂往哪“卷”?

裁员只是大厂踌躇、转型的缩影&#xff0c;无论是林平还是背后的公司、行业&#xff0c;都亟需给未来一个确定的答案。 从2021年冬天开始&#xff0c;大厂裁员的消息有如“击鼓传花”般&#xff0c;不断冲击着圈内外网友们的神经&#xff0c;那可能是“裁员”这个话题自互联网…

css中>>>、/deep/、::v-deep的作用和区别,element-ui自定义样式

文章目录 一、前言1.1、/deep/1.2、::v-deep1.3、>>> 二、区别三、总结四、最后 一、前言 1.1、/deep/ 在style经常用scoped属性实现组件的私有化时&#xff0c;要改变element-ui某个深层元素&#xff08;例如.el-input__inner&#xff09;或其他深层样式时&#xf…

JOSEF约瑟 大容量中间继电器 RXMH2 RK223 067 DC220V 带底座

系列型号 RXMH2 RK 223 067大容量中间继电器&#xff1b; RXMH2 RK 223 068大容量中间继电器&#xff1b; RXMH2 RK 223 069大容量中间继电器&#xff1b; RXMH2 RK 223 070大容量中间继电器&#xff1b; 一、用途 RXMH2系列大容量中间继电器用于工业自动化控制及电力系统二次…

app重新签名之后,teamID会改变吗?

哈喽大家好&#xff0c;我是咕噜美乐蒂&#xff0c;很高兴又见面啦&#xff01; 在iOS和macOS开发中&#xff0c;我们经常需要对应用进行重新签名&#xff0c;以便在发布或测试时更改应用的证书或代理。在重新签名应用程序时&#xff0c;可能会出现teamID变化的情况。本文将探…