vue3生成点选验证码,前端校验

news2025/1/23 22:27:36

 先看效果图

 

 

  verify.vue:源码{用时一天半的破轮子}!!!!!!!!!!!!

<template>
	<div class="outside" ref="outside">
		<div class="action-tip">请依次点
			<p class="action-target">
				"{{words[0]}}""{{words[1]}}""{{words[2]}}""{{words[3]}}"
			</p>
			<span id="closeIcon" @click="closeVerify">
				<el-icon><Close /></el-icon>
			</span>
		</div>
		<div class="display-area" ref="content">
			<!-- 风景背景图 -->
			<!-- style="display: none;" -->
			<img id="tulip" class="bg-picture" :src="background" style="display: none;"  alt="背景图片">
			<canvas  class="bg-picture" @click="checkPointAtCanvas" id="myCanvas"></canvas>

			
		</div>
		<div v-show="isSucceed" class="tip">
			<div class="tip-content">验证成功!</div>
		</div>
		<center>
			<div class="msg" v-show="!isSucceed">
				<el-button link type="plain" @click="changeAll()">
					<el-icon><Refresh /></el-icon>
					看不清,换一张
				</el-button>
			</div>
		</center>

		<div v-show="falseTip" class="tip">
			<div class="tip-content">验证失败,请重新验证</div>
		</div>
	</div>
</template>

<script>
	export default {
		data() {
			return {
				num: 1,
				x: null,
				y: null,
				py: 10, //偏移量
				lastPic: null, //上一个图片下标,防止重复之后导致刷新不重载
				pointArr: [],
				bgPictures: [
					{ id: '0',src: require('@/assets/login/1.png')},
					{ id: '1',src: require('@/assets/login/2.png')},
					{ id: '2',src: require('@/assets/login/3.png')},
					{ id: '3',src: require('@/assets/login/4.png')},
					{ id: '4',src: require('@/assets/login/5.png')},
					{ id: '5',src: require('@/assets/login/6.png')},
					{ id: '6',src: require('@/assets/login/7.png')},
					{ id: '7',src: require('@/assets/login/8.png')},
					{ id: '8',src: require('@/assets/login/9.png')},
					{ id: '9',src: require('@/assets/login/10.jpg')},
					{ id: '10',src: require('@/assets/login/11.jpg')},
					{ id: '11',src: require('@/assets/login/12.jpg')},
					{ id: '12',src: require('@/assets/login/13.png')},
					{ id: '13',src: require('@/assets/login/18.jpg')},
					{ id: '14',src: require('@/assets/login/23.jpg')}
					
				],
				background: "",
				codeCharacter: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'A', 'B', 'C', 'D', 'E', 'F'], //16进制数
				words: new Array(4), //储存四个汉字
				choseOrder: [], //用户选择的顺序
				falseTip: false, //用户验证失败,但是仍有次数,自动刷新时的提示
				isSucceed: false, //用户验证成功
			}
		},
		watch: {
			choseOrder: {
				deep: true,
				handler(newval) {
					console.log(this.$deepCopy(this.choseOrder),this.$deepCopy(this.pointArr)) 
					for(var i = 0;i< this.choseOrder.length;i++ ){
						var xMinTrue =  this.pointArr[i].x  - this.py;
						var xMaxTrue =  this.pointArr[i].x + 34  + this.py;
						var yMinTrue =  this.pointArr[i].y - 40  - this.py;
						var yMaxTrue =  this.pointArr[i].y  + this.py;
						console.log(xMinTrue,xMaxTrue,yMinTrue,yMaxTrue)
						if(this.choseOrder[i].x < xMinTrue || this.choseOrder[i].x > xMaxTrue ){
							this.falseTip = true
						}
						if(this.choseOrder[i].y < yMinTrue || this.choseOrder[i].y > yMaxTrue ){
							this.falseTip = true
						}
					}
					if(this.falseTip == true){
						setTimeout(()=>{
							this.changeAll();
							this.falseTip = false;
							this.clearPoint();
						},2000)
					}
					if(!this.falseTip && this.choseOrder.length==4){
						this.isSucceed = true
					}
				}
			}
		},
		beforeMount() {
			this.chooseFourWords();
		},
		mounted() {
			this.chooseBGP();
		},
		beforeDestroy() {},
		methods: {
			
			checkPointAtCanvas(event){
				this.x = event.offsetX+34;
				this.y = event.offsetY+12;
				this.createPoint();
				//减去padding偏移量
				this.choseOrder.push({
					x: this.x-34,
					y: this.y-12
				})
			},
			//创建坐标点
			createPoint() {
				document.getElementsByClassName("display-area")[0].insertAdjacentHTML('beforeEnd', '<div class="point-area" style="background-color:#1abd6c;color:#fff;z-index:9999;width:30px;height:30px;text-align:center;line-height:30px;border-radius: 50%;position:absolute;top:'+parseInt(this.y-10)+'px;left:'+parseInt(this.x-10)+'px;">'+this.num+'</div>');
				this.num = this.num+1;
			},
			//随机选择四个文字,加入到 words 数组
			chooseFourWords() {
				let arr = new Array();
				var indexArr = [];
				var fontStr = '天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔龙师火帝鸟官人皇始制文字乃服衣裳推位让国有虞陶唐吊民伐罪周发殷汤坐朝问道垂拱平章爱育黎首臣伏戎羌遐迩体率宾归王';	//不重复的汉字
				while (indexArr.length<4){
					var index = this.rand(0,fontStr.length);
					if(indexArr.indexOf(index)==-1 && fontStr.substring(index,index+1)!=""){
						indexArr.push(index);
						arr.push(fontStr.substring(index,index+1))
					}
				}
				console.log(indexArr,arr)
				this.words = arr;
			},
			rand(m, n)  {
			    return Math.ceil(Math.random() * (n-m+1) + m-1)
			},
			//随机获取四个坐标点
			getFourPoint(){
				var arr = [];
				var x1 = this.rand(30, 80);
				var x2 = this.rand(130, 180);
				var x3 = this.rand(230, 280);
				var x4 = this.rand(314, 350);
				var y1 = this.rand(105, 176);
				var y2 = this.rand(105, 176);
				var y3 = this.rand(105, 176);
				var y4 = this.rand(105, 176);
				arr.push({ x: x1, y: y1});
				arr.push({ x: x2, y: y2});
				arr.push({ x: x3, y: y3});
				arr.push({ x: x4, y: y4});
				this.pointArr = arr
			},
			//更换背景图片
			chooseBGP() {
				var index = Math.round(Math.random() * (this.bgPictures.length-1));
				
				while (this.lastPic == index){
					index = Math.round(Math.random() * (this.bgPictures.length-1));
				}
				this.lastPic = index;
				this.background = this.bgPictures[index].src;
				this.getFourPoint();
				var _this = this;
				
				var canvas=document.getElementById("myCanvas");
				var ctx=canvas.getContext("2d");
				var img=document.getElementById("tulip");
				
				var size = 400;
				canvas.style.width = size + "px";
				canvas.style.height = 260 + "px";
	
				var scale = 1;
				canvas.width = Math.floor(size * scale);
				canvas.height = Math.floor(260 * scale);
				ctx.scale(scale, scale);
				ctx.clearRect(0, 0, size, 260);
				img.onload = function(){
					ctx.drawImage(this, 0, 0, size, 260);
					for(var i = 0;i<_this.pointArr.length;i++){
						ctx.font = "34px STXingkai";
						ctx.fillStyle = '#' + ('00000' + (Math.random() * 0x1000000 << 0).toString(16)).substr(-6);
						ctx.fillText(_this.words[i],_this.pointArr[i].x, _this.pointArr[i].y);
					}
					
				}
			},
			//换一张    重新部署
			changeAll() {
				this.chooseFourWords();
				this.chooseBGP();
				this.numtip = [];
				this.choseOrder = [];
				this.clearPoint();
			},
			clearPoint(){
				document.querySelectorAll(".point-area").forEach(x=>{
					document.querySelector(".display-area").removeChild(x)
				});
				this.choseOrder = [];
				this.num = 1;
			},
			closeVerify(){
				this.$emit("closeVerify")
			}
			
			
		},
	}
</script>

<style scoped="scoped">
	/* 最外侧轮廓 */
	.outside {
		width: 468px;
		height: 360px;
		position: absolute;
		border-radius: 6px;
		background-color: #FFFFFF;
	}

	/**上方图片展示区域 */
	.display-area {
		/* width: 100%; */
		/* height: 75%; */
		position: relative;
		overflow: hidden;
		padding: 34px;
		padding-top: 12px;
		padding-bottom: 18px;
	}

	.bg-picture {
		width: 100%;
		height: 260px;
		z-index: 2;
		border-radius: 10px;
	}

	/**汉字span */
	.word-img {
		display: inline-block;
		box-sizing: border-box;
		padding: 0.1em;
		font-size: 2rem;
		font-weight: 700;
		border-radius: 0.3em;
		/* background: -webkit-gradient(linear, 0 0, 0 bottom, from(rgba(241, 210, 240, 0.644)), to(rgba(0, 0, 255, 0))); */
		position: absolute;
		z-index: 4;
	}

	/**底部提示用户操作区 换一张按钮放置区 */
	.msg {
		position: absolute;
		bottom: 8px;
		width: 100%;
		z-index: 8;
		display: flex;
		padding-left: 34px;
	}

	p {
		margin-top: 0px;
		margin-bottom: 0px;
	}

	.action-target {
		/* line-height: 1.6; */
		font-size: 20px;
	}

	

	/**数字顺序提示 */
	.number-tip {
		width: 1.3rem;
		height: 1.3rem;
		background-color: #fff;
		border-radius: 90px;
		opacity: 0.9;
		z-index: 8;
		position: fixed;
	}

	.word-img,button,.number-tip:hover {
		cursor: pointer;
	}

	/**判断用户验证结果的提示 */
	.tip {
		position: absolute;
		left: 0px;
		top: 0px;
		width: 100%;
		height: 100%;
		background-color: rgba(128, 127, 127, 0.8);
		font-size: large;
		z-index: 9999;
	}

	.tip-content {
		position: absolute;
		left: 50%;
		top: 50%;
		transform: translate(-50%, -50%);
		font-size: 2rem;
		font-weight: bolder;
		z-index: 15;
	}

	.font {
		font: 34px STXingkai;
	}

	.action-tip {
		display: flex;
		    align-items: center;
		    justify-content: center;
		    padding: 20px 10px;
		    padding-bottom: 0px;
		    font-size: 18px;
	}
	/deep/ .el-button.is-link{
		font-size: 16px;
	}
	#closeIcon{
		position: absolute;
		    right: 10px;
		    font-size: 24px;
		    top: 5px;
		    cursor: pointer;
	}
	#closeIcon:hover{
		color: #3465FA;
	}
</style>

使用方法

<verify @closeVerify="closeVerify"></verify>
import verify from "@/components/verify"
data() {
    return {
        verifyMark: false
    }
},
components:{
    verify
},
methods: {
    closeVerify(){
        this.verifyMark = false;
    }
}

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

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

相关文章

寻路算法小游戏

寻路算法小demo 寻路算法有两种&#xff0c;一种是dfs 深度优先算法&#xff0c;一种是 dfs 深度优先算法 深度优先搜索的步骤分为 1.递归下去 2.回溯上来。顾名思义&#xff0c;深度优先&#xff0c;则是以深度为准则&#xff0c;先一条路走到底&#xff0c;直到达到目标。这…

spring的核心技术---bean的生命周期加案例分析详细易懂

目录 一.spring管理JavaBean的初始化过程&#xff08;生命周期&#xff09; Spring Bean的生命周期&#xff1a; 二.spring的JavaBean管理中单例模式及原型&#xff08;多例&#xff09;模式 2.1 . 默认为单例&#xff0c;但是可以配置多例 2.2.举例论证 2.2.1 默认单例 2.2…

什么是报表测试?报表测试有哪些?

报表测试主要分为&#xff1a;报表界面测试、报表安全性、报表准确性、报表展示速度&#xff08;也就是性能&#xff09;。 数据准确性测试 报表测试的系统分为两类&#xff0c;一类是业务系统中&#xff0c;带有统计分析功能模块&#xff0c;该模块中包含分析报表&#xff0c;…

Docker碎碎念

docker和虚拟机的区别 虚拟机&#xff08;VM&#xff09;是通过在物理硬件上运行一个完整的操作系统来实现的。 每个虚拟机都有自己的内核、设备驱动程序和用户空间&#xff0c;它们是相互独立且完全隔离的。 虚拟机可以在不同的物理服务器之间迁移&#xff0c;因为它们是以整…

【仿写tomcat】六、解析xml文件配置端口、线程池核心参数

线程池改造 上一篇文章中我们用了Excutors创建了线程&#xff0c;这里我们将它改造成包含所有线程池核心参数的形式。 package com.tomcatServer.http;import java.util.concurrent.*;/*** 线程池跑龙套** author ez4sterben* date 2023/08/05*/ public class ThreadPool {pr…

社科院与美国杜兰大学金融管理硕士项目——畅游于金融世界

随着社会经济的不断发展&#xff0c;职场竞争愈发激烈&#xff0c;很多同学都打算通过报考研究生来实现深造&#xff0c;提升自己的综合能力和竞争优势&#xff0c;获得优质的证书。而对于金融专业的学生和在职人员来说&#xff0c;社科院与美国杜兰大学金融管理硕士项目是一个…

ElasticSearch7.x + kibana7.x使用记录

目录 查询所有索引 查询索引的mapping信息 添加索引的同时添加mapping 在原有基础上新增字段 旧的索引迁移到新的索引&#xff08;使用场景&#xff1a;数据迁移、索引优化、数据转换&#xff09; 查询索引下的文档总数 场景1&#xff1a;某一个字段的值是数组&#xff0…

数据的深海潜行:数据湖、数据仓库与数据湖库之间的微妙关系

导言&#xff1a;数据的重要性与存储挑战 在这个信息爆炸的时代&#xff0c;数据已经成为企业的核心资产&#xff0c;而如何高效、安全、便捷地存储这些数据&#xff0c;更是每个组织面临的重大挑战。 数据作为组织的核心资产 数据在过去的几十年里从一个辅助工具演变成企业的…

视频怎么转换成gif表情包?三步完成视频在线转gif

小伙伴们在使用gif表情包的时候&#xff0c;都会注意到有些是视频片段&#xff0c;其实视频转换成gif动图已经很常见了&#xff0c;今天就来给大家演示一下使用视频转gif工具&#xff08;https://www.gif.cn&#xff09;来将视频在线转gif&#xff0c;一起来学习一下吧。 打开…

ps由于找不到msvcp140.dll无法继续执行需要怎么处理

最近在使用Photoshop时遇到了一个问题&#xff0c;它提示我缺少了msvcp140.dll文件。这让我无法正常使用Photoshop&#xff0c;非常困扰。然而&#xff0c;通过一番尝试和研究&#xff0c;我终于成功修复了这个问题&#xff0c;小编就把修复方法分享给大家。 ps为什么会由于找不…

【文生图系列】Denoising Diffusion Probabilistic Models论文解读

文章目录 扩散模型扩散过程反向过程优化目标 训练过程参考 此篇论文是DDPM的奠基之作&#xff0c;后续扩散模型相关论文都基本继承了前向加噪-反向降噪-训练这样的框架。论文全是公式&#xff0c;理解起来好难好难。 【文生图系列】基础篇-马尔可夫链 【文生图系列】基础篇-变分…

springboot中的properties配置文件:MySQL密码添加双引号会报错

在springboot项目中&#xff0c;如果使用的配置文件是properties 格式&#xff0c;那么给属性的值添加双引号可能会出错&#xff0c;比如给MySQL的密码添加双引号&#xff1a; 我们在调试模式下&#xff0c;一探究竟&#xff1a; 然后在其poolProperties中查看&#xff0c;密…

如何快速在vscode中实现不同python文件的对比查看

总体而言&#xff1a;两种方式。一种是直接点击vscode右上角的图标&#xff08;见下图&#xff09;。 另一种方式就是使用快捷键啦“**Ctrl**”&#xff0c;用的时候选中想要对比的python文件&#xff0c;然后快捷键就可以达到下图效果了&#xff1a; 建议大家直接使用第二种…

Java课题笔记~ SpringBoot基础配置

二、基础配置 1. 配置文件格式 问题导入 框架常见的配置文件有哪几种形式&#xff1f; 1.1 修改服务器端口 http://localhost:8080/books/1 >>> http://localhost/books/1 SpringBoot提供了多种属性配置方式 application.properties server.port80 applicati…

电动汽车太秀了!用一个技巧搞定了蓄电池!

当涉及能源存储和供应&#xff0c;特别是在太阳能、电动车和不间断电源等领域&#xff0c;蓄电池无疑是关键的组成部分。然而&#xff0c;蓄电池的状态、性能和健康状况对于系统的可靠性和效率至关重要。 蓄电池监控通过实时监测、数据分析和预警功能&#xff0c;它提供了更高效…

PCB老化测试注意事项和操作流程

PCB老化测试是为了评估PCB在实际应用环境中的可靠性和寿命而进行的测试。以下是PCB老化测试的一般步骤和一些注意事项&#xff1a; 步骤&#xff1a; 1. 确定老化条件&#xff1a;根据实际应用环境和需求&#xff0c;确定老化测试的条件&#xff0c;如温度、湿度、电压等。这些…

基于Redis实现关注、取关、共同关注及消息推送(含源码)

微信公众号访问地址&#xff1a;基于Redis实现关注、取关、共同关注及消息推送(含源码) 推荐文章&#xff1a; 1、springBoot对接kafka,批量、并发、异步获取消息,并动态、批量插入库表; 2、SpringBoot用线程池ThreadPoolTaskExecutor异步处理百万级数据; 3、为什么引入Rediss…

【3Ds Max】可编辑多边形“点”层级的简单使用

目录 简介 示例 &#xff08;1&#xff09;移除 &#xff08;2&#xff09;断开 &#xff08;3&#xff09;焊接 &#xff08;4&#xff09;挤出 &#xff08;5&#xff09;切角 &#xff08;6&#xff09;目标焊接 &#xff08;7&#xff09;连接 简介 在3ds Max中&…

Golang使用MinIO

最近在使用Golang做了一个网盘项目&#xff08;学习&#xff09;&#xff0c;文件存储一直保存在本地&#xff08;各厂商提供的oss贵&#xff09;&#xff0c;所以就在思考怎么来处理这些文件&#xff0c;类似的方案很对hdfs、fastdfs&#xff0c;但这其中MinIO是最近几年比较火…