vue3 顺序点击文字验证组件


实现效果图:
文字添加下划线是因为我是数字校验,需要分清旋转的6和9的区别。
在这里插入图片描述

点击效果:

注: 具体样式可以根据需求变更

在这里插入图片描述
在这里插入图片描述

实现思路:

  1. 组件传值获取到需要的随机文字
  2. 对文字随机位置进行生成,一个字生成的坐标存储起来进行区间比对,如果重合则进行递归调用
  3. 添加点击事件添加文字坐标,样式进行等随机生成
  4. 点击文字将当前文字排列拼接起来,长度一致后进行比对
  5. 然后根据结果运行自义定函数

注:如有问题欢迎进行留言讨论

<template>
	<view class="background">
		<view class="imageView">
			<view class="imageView-top">
				<text>请依次点击绑定的手机号码后四位</text>
				<view class="">
					<uni-icons type="refreshempty" size="20" style="margin-right: 20rpx;" @click="arrangementRandom">
					</uni-icons>
					<uni-icons type="closeempty" size="20" @click="closeDialog"></uni-icons>
				</view>
			</view>
			<view class="box">
				<!-- 上面盒子 -->
				<view class="topbox">
					<!-- 图片 -->
					<img class="topboximg"
						src="../static/image/Path.png"
						alt="" />

					<!-- 文字 -->
					<view class="toptext">
						<text class="textspan"
							:style="{backgroundColor:item.backgroundColor,color:item.color,left:item.left,top:item.top,fontSize:item.fontSize,transform:item.transform}"
							@click.stop="textClick(index)" v-for="(item,index) in options.textList"
							:key="index">{{item.title}}</text>
					</view>
				</view>
			</view>
		</view>
	</view>
</template>

<script lang="ts">
	import {
		defineComponent,
		ref,
		watch,
		reactive,
		computed,
		toRefs
	} from 'vue'
	import {
		onShow
	} from "@dcloudio/uni-app"

	export default defineComponent({
		name: 'dsVeryfyText',
		props: {
			verifyText: {
				type: String || Number,
				default: "1469"
			}
		},
		emits: ["closeDialog", 'success'],
		setup(props: any, context) {
			const verifyText = computed(() => props.verifyText)
			const options: any = reactive({
				imgWidth: 620, // 文字底图宽度
				imgHeight: 375, // 文字底图高度
				textspanW: 90, // 最大文字宽度
				textspanH: 90, // 最大文字高度
				clickText: "", // 点击文字排列
				leftList: [], // 文字随机坐标列表
				topList: [], // 文字随机坐标列表
				textList: [{
					title: "1",
					backgroundColor: "",
					color: "",
					left: "",
					top: "",
					fontSize: "",
					transform: ""
				}]
			})


			/**
			 * 函数
			 * */
			// 初始化
			function init() {
				let list = verifyText.value.split("");
				options.textList = list.map(item => {
					return {
						title: item,
						backgroundColor: "",
						color: "",
						left: "",
						top: "",
						fontSize: "",
						transform: ""
					}
				})
				arrangementRandom()
			}

			// 排列随机
			function arrangementRandom() {
				
				// (点击更换颜色功能)
				options.clickText = "";
				options.topList = [];
				options.leftList = [];
				// (随机文字坐标功能)
				options.textList.forEach(function(item: any) {
					const {
						top,
						left
					} = getItemRandomTopLeft();
					item.backgroundColor = ""; // 清空颜色
					item.left = left + "rpx";
					item.top = top + "rpx";
					item.color = getRandomColor();
					item.fontSize = Math.floor(Math.random() * 60 + 30) + "rpx";
					item.transform = "rotate(" + Math.random() * 360 + "deg)";
				});
			}

			// 文字点击事件
			function textClick(index: number) {
				console.log(options.textList[index].title);
				options.textList[index].backgroundColor = "red";
				options.textList[index].color = "#fff";
				// 验证是否正确
				options.clickText = options.clickText + options.textList[index].title

				// 当前最后一位
				if (options.clickText.length == verifyText.value.length) {
					if (options.clickText == verifyText.value) {
						uni.showModal({
							title: "提示",
							content: "验证成功",
							showCancel: false,
							success() {
								context.emit("success")
							}
						})
					} else {
						uni.showModal({
							title: "提示",
							content: "验证失败,请重试",
							showCancel: false,
							success() {
								arrangementRandom()
							}
						})
					}
				}
			}

			// 生成随机的RGB颜色值
			function getRandomColor() {
				var r = Math.floor(Math.random() * 256); // 0 到 255 之间的随机整数
				var g = Math.floor(Math.random() * 256);
				var b = Math.floor(Math.random() * 256);

				// 将RGB值转换为十六进制格式
				var hexColor = "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);

				return hexColor;
			}

			// 判断生成的边界值
			function getItemRandomTopLeft() {
				var left = Math.abs(Math.floor(Math.random() * (options.imgWidth - options.textspanW) - 10));
				var top = Math.abs(Math.floor(Math.random() * (options.imgHeight - options.textspanH) - 10));
				if (options.leftList.length > 0) {
					for (let index = 0; index < options.leftList.length; index++) {
						// 重合判断
						if (left >= options.leftList[index] - (options.textspanW / 4 * 3) && left < options
							.leftList[index] + (options.textspanW / 4 * 3) && top >= options.topList[index] - (
								options.textspanW / 4 * 3) && top <
							options.topList[index] + (options.textspanW / 4 * 3)) {
							return getItemRandomTopLeft();
						}
					}
				}

				options.leftList.push(left);
				options.topList.push(top);
				return {
					left,
					top
				};
			}

			// 关闭弹窗
			function closeDialog() {
				context.emit("closeDialog")
			};

			watch(() => verifyText.value, (newVal, oldValue) => {
				if (!!newVal) {
					init()
				}
			}, {
				immediate: true
			})

			return {
				...toRefs(props),
				options,
				textClick,
				closeDialog,
				arrangementRandom
			}
		}
	})
</script>

<style lang="scss" scoped>
	.box {
		width: 620rpx;
		height: 375rpx;
		position: relative;
		box-shadow: 1px 1px 5rpx #2e2e2e;
	}

	/* 上面盒子 */
	.topbox {
		position: relative;
	}

	/* 图片 */
	.topboximg {
		width: 100%;
		height: 375rpx;
		/* padding: 24rpx; */
	}

	/* 刷新 */
	.refresh {
		position: absolute;
		right: 4rpx;
		top: 0px;
		color: white;
		font-weight: bolder;
		font-size: 30rpx;
		cursor: pointer;
	}

	/* 文字 */
	.toptext {
		/* position: relative; */
		display: flex;
		justify-content: space-between;

		cursor: pointer;
	}

	.textspan {
		position: absolute;
		top: 0;
		left: 0;
		text-decoration: underline;
	}

	.toptext>text {
		color: rgb(204, 255, 0);
		font-weight: 500;
		font-size: 18rpx;
	}

	.background {
		position: fixed;
		top: 0;
		width: 100vw;
		height: 100vh;
		background: rgba(0, 0, 0, .7);
		display: flex;
		justify-content: center;
		align-items: center;
		z-index: 999;
	}

	.imageView {
		//width: 560rpx;
		background: #fff;
		border-radius: 10rpx;
		padding: 40rpx;

		&-top {
			display: flex;
			justify-content: space-between;
			margin-bottom: 30rpx;
		}

		&-bottom {
			position: relative;
			width: 100%;
			height: 55rpx;
			line-height: 55rpx;
			background: #e4e7eb;
			text-align: center;
			font-size: 16rpx;
			color: #666;
		}

		.sliceblock {
			position: absolute;
			top: 0;
			box-shadow: 0 0 3rpx rgb(0 0 0 / 30%);
			background: #fff;
			width: 55rpx;
			height: 55rpx;
		}
	}
</style>

Vue3中的拖拽排序组件通常是一种基于HTML5 Drag and Drop API的UI组件,它允许用户通过鼠标或触摸操作,在界面上对元素进行拖放交互,实现列表、卡片等元素的动态顺序调整。Vue3提供了诸如Vuedraggable、vue-draggable-resizable这样的第三方库,它们简化了在Vue项目中集成拖拽排序功能的过程。 这类组件一般包含以下几个关键特性: 1. **事件监听**:如`dragstart`, `drag`, `dragend`等,用于处理拖动开始、移动以及结束的不同阶段。 2. **数据绑定**:将实际的数据源绑定到组件上,当列表内容改变时,可以实时同步到后端或状态管理工具(如Vuex)。 3. **放置策略**:支持自定义放置规则,比如禁止在列表外放置,只允许在同一层级内交换位置等。 4. **回调函数**:提供钩子函数让用户在特定事件发生时执行自定义操作,比如验证新的排列顺序是否合法。 使用Vue3拖拽排序组件的一个简单示例: ```html <template> <vuedraggable v-model="items" :options="{ group: &#39;group&#39;, draggable: &#39;draggableItem&#39; }"> <div class="draggableItem" v-for="(item, index) in items" :key="index">{{ item.name }}</div> </vuedraggable> </template> <script> import Vuedraggable from "vuedraggable"; export default { components: { Vuedraggable }, data() { return { items: [ { name: &#39;A&#39;, draggableItem: true }, { name: &#39;B&#39;, draggableItem: true }, // 更多项... ], group: &#39;default&#39;, }; }, }; </script> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值