实现效果图:
文字添加下划线是因为我是数字校验,需要分清旋转的6和9的区别。
点击效果:
注: 具体样式可以根据需求变更
实现思路:
- 组件传值获取到需要的随机文字
- 对文字随机位置进行生成,一个字生成的坐标存储起来进行区间比对,如果重合则进行递归调用
- 添加点击事件添加文字坐标,样式进行等随机生成
- 点击文字将当前文字排列拼接起来,长度一致后进行比对
- 然后根据结果运行自义定函数
注:如有问题欢迎进行留言讨论
<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>