验证码输入组件实现思路
效果如图

主要实现思路
- 使用label的for属性绑定input的id, input使用定位, 挪出屏幕外面不进行展示
- input获取焦点时, 给label内元素添加边框颜色
- 给下标为当前input的value长度元素添加深一点的边框颜色, 同时展示光标
- 观察input的value长度, 同时抛出change事件
- 观察input的value长度, 如果value长度等于给到的长度抛出finish事件
- input失去焦点时去掉边框颜色
思路逐行分析
思路第一条
label元素的特性为for绑定的表单元素,或者它内部的表单元素, 点击label元素的区域会自动聚焦或者选中, 利用这一特性我们可以实现很多效果, 比如美化表单元素等等
将input移出屏幕外是因为它不需要展示 只需要负责接收输入内容就可以了
思路第二条
首先看dom结构, label的for属性指向了input,此时点击label和点击input是同样的聚焦效果, label内放置了div, 也就是方框, 当聚焦时给所有方框添加一个focus类名控制聚焦样式
<template>
<div class="validate">
<label :for="id" class="validate_label">
<div class="validate_item"
v-for="(item,index) in prop.len"
:key="item"
:class="{active:hold && lastItem===index,focus: hold}"
>
{{val[index]}}
<div class="holder"></div>
</div>
</label>
<input class="validate_inp"
type="text" :id="id"
@focus="focus"
@blur="blur"
v-model="val"
:maxlength="prop.len"
>
</div>
</template>
思路第三条
给下标为input的value长度的元素添加深一点的边框颜色以及展示它内部的光标
这里是这么计算的
如果input的value为123,那么此时应该是第四个元素要进行输入, 第四个元素的下标为3, input的value的长度为3
由此得出 lastItem = input.value.length
思路第四条和思路第五条
这里没有考虑外面传入v-model从而进行修改, 采用事件抛出的方式进行通知父组件
主要抛出change(值改变)事件和finish(当前值的长度等于传入长度)事件, 主要实现逻辑如下
watch([val], () => {
emit("change", val.value)
if (lastItem.value >= prop.len) {
emit("finish",val.value)
}
})
vue3实现整体代码
<template>
<div class="validate">
<label :for="id" class="validate_label">
<div class="validate_item"
v-for="(item,index) in prop.len"
:key="item"
:class="{active:hold && lastItem===index,focus: hold}"
>
{{val[index]}}
<!--光标-->
<div class="holder"></div>
</div>
</label>
<input class="validate_inp"
type="text" :id="id"
@focus="focus"
@blur="blur"
v-model="val"
:maxlength="prop.len"
>
</div>
</template>
<script setup lang="ts">
import {computed, ref, watch} from "vue";
interface propType {
len?: number; // 验证码长度 默认值为4
}
const prop = withDefaults(defineProps<propType>(), {
len: 4
})
// 接收两个事件
// finish 当前值长度 === 验证码长度
// change 当前值修改
const emit = defineEmits(["finish", "change"])
// id全局唯一 防止重复使用随机数生成
// 这里比较有意思 单独说一下
// Math.random() 生成0-1的随机数
// 将他们转成String类型, 并且使用split进行切割 取出后半部分小数
// 将取出的数字再转换为 Number类型 调用Number.toString(16) 将他转为16进制数字 这时候随机id就搞定了
const id = ref(Number(String(Math.random()).split(".")[1]).toString(16))
// input绑定值
const val = ref<number | string>("")
// 根据值长度进行计算当前应该被选中框的下标
const lastItem = computed(() => {
return val.value.toString().length
})
// input是否为聚焦状态
const hold = ref<boolean>(false)
// 观察值的改变并且抛出 "finish", "change" 事件
watch([val], () => {
emit("change", val.value)
if (lastItem.value >= prop.len) {
emit("finish",val.value)
}
})
// 聚焦状态下 修改hold为true
const focus = () => {
hold.value = true
}
// 失焦状态下 修改hold为false
const blur = () => {
hold.value = false
}
</script>
<style lang="scss" scoped>
// 给input 定位出屏幕外
.validate_inp{
position: fixed;
top: -520px;
left: -520px;
}
.validate {
&_label {
display: flex;
align-items: center;
}
//方框样式
&_item {
display: flex;
width: 50px;
height: 50px;
border: 1px solid #888888;
margin-right: 15px;
align-items: center;
justify-content: center;
transition: all 0.3s;
// 光标样式
.holder{
width: 2px;
height: 40%;
background: #b4b4b4;
animation: holderSnick 600ms infinite alternate;
display: none;
}
// 光标动画
@keyframes holderSnick {
0%{
opacity: 0;
}
100%{
opacity: 1;
}
}
// 获取焦点的通用状态
&.focus{
border-color: #93b2e8;
}
// 当前为应该输入的框的状态
&.active {
border-color: #1963e8;
// 展示光标
.holder{
display: block;
}
}
}
}
</style>

该文详细介绍了如何使用Vue3构建验证码输入组件,利用label的for属性和input的定位实现输入效果。组件在input获取焦点时改变边框颜色,根据input的value长度动态更新激活的方框,并通过事件抛出值的改变和完成状态。CSS用于美化和控制光标动画。

3114

被折叠的 条评论
为什么被折叠?



