验证码输入组件实现思路

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

验证码输入组件实现思路

效果如图
在这里插入图片描述

主要实现思路

  1. 使用label的for属性绑定input的id, input使用定位, 挪出屏幕外面不进行展示
  2. input获取焦点时, 给label内元素添加边框颜色
  3. 给下标为当前input的value长度元素添加深一点的边框颜色, 同时展示光标
  4. 观察input的value长度, 同时抛出change事件
  5. 观察input的value长度, 如果value长度等于给到的长度抛出finish事件
  6. 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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值