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

news/2024/7/10 1:07:46 标签: 点选验证码, vue

 先看效果图

 

 

  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.niftyadmin.cn/n/4949058.html

相关文章

无法打开程序因为msvcp140.dll丢失,msvcp140.dll丢失的解决方法

前几天看到有小伙伴再问什么是msvcp140.dll文件&#xff0c;相信很多人都不知道这是什么吧&#xff0c;如果电脑msvcp140.dll文件丢失的话会怎么样呢&#xff1f;丢失了应该如何找回呢&#xff1f;其实这些都是一些比较常见的电脑知识&#xff0c;我们是需要去了解一下的&#…

iOS自定义下拉刷新控件

自定义下拉刷新控件 概述 用了很多的别人的下拉刷新控件&#xff0c;想写一个玩玩&#xff0c;自定义一个在使用的时候也会比较有意思。使应用更加的灵动一些&#xff0c;毕竟谁不喜欢各种动画恰到好处的应用呢。 使用方式如下&#xff1a; tableview.refreshControl XRef…

seaborn color palette 调色板颜色图

Here is a list of the Color Brewer palettes, with their names for easy reference: sns.lineplot(datanormal_df, palettesns.color_palette(paletteSet1, n_colors1))

基于C#的消息处理的应用程序 - 开源研究系列文章

今天讲讲基于C#里的基于消息处理的应用程序的一个例子。 我们知道&#xff0c;Windows操作系统的程序是基于消息处理的。也就是说&#xff0c;程序接收到消息代码定义&#xff0c;然后根据消息代码定义去处理对应的操作。前面有一个博文例子( C#程序的启动显示方案(无窗口进程发…

ARM M33架构入门

概述 Arm Cortex-M33核心处理器专为需要高效安全或数字信号控制的物联网和嵌入式应用而设计。该处理器具有许多可选功能&#xff0c;包括数字信号处理扩展 (DSP)、用于硬件强制隔离的TrustZone 安全性、内存保护单元 (MPU)和浮点单元 (FPU)。 Cortex-M33 的性能比 Cortex-M…

Java设计模式 (三) 代理设计模式

什么是代理设计模式? 代理设计模式是一种结构型设计模式&#xff0c;它允许创建一个代理对象&#xff0c;用于控制对其他对象的访问。代理模式通常用于在访问对象时添加一些附加操作&#xff0c;而不是直接访问真实对象。代理模式可以在不改变原始类代码的情况下&#xff0c;通…

Spring中Bean的生命周期以及Bean的单例与多例模式

一. Bean的生命周期 bean的生命周期可以表达为&#xff1a;bean的定义➡bean的初始化➡bean的使用➡bean的销毁 Bean的初始化过程 1&#xff09;通过XML、Java annotation&#xff08;注解&#xff09;以及Java Configuration&#xff08;配置类&#xff09; 等方式加载Bea…

Grafana监控大盘配置教程

1、新建大盘 2、输入指标和大盘名 若是Time series类型&#xff0c;则到此就可以看到数据&#xff1b;若是Table类型则进行下一步 3、修改大盘类型为Table 4、修改指标输出 Transformation functions&#xff1a;Transform data | Grafana documentation Filter by name——…