目录
一、前言:
1.1 背景:
1.1.1 为了和app的交互保持一致,所以h5和app内嵌h5也需要保持原生验证码输入交互
1.1.2 为什么使用 type = number 呢,因为在嵌在app内时,需要唤起数字键盘
1.2 web端访问效果:
二、代码
2.1 代码背景:vue3 + ts
2.1.1 css
.digits {
margin-top: 12px;
position: relative;
margin-bottom: 24px;
}
.digits input {
position: absolute;
z-index: -11;
top: -100px;
left: 5px;
outline: none;
resize: 0;
border: none;
opacity: 0;
}
.digits .digits-num p{
box-sizing: border-box;
width: 48px;
height: 48px;
background-color: pink;
border: 1px solid #F6F6F6;
border-radius: 8px;
color: #000;
text-align: center;
font-size: 20px;
font-weight: 700;
line-height: 1.4;
position: relative;
-webkit-tap-highlight-color: transparent;
}
.digits .digits-num p + p {
margin-left: 8px;
}
.digits .digits-num .dots span {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
background-color: #000;
}
.digits .digits-num .dot1 {
animation: cursor1 1.2s infinite;
}
.digits .digits-num .dot2 {
animation: cursor1 1.2s infinite 0.1s;
}
.digits .digits-num .dot3 {
animation: cursor1 1.2s infinite 0.2s;
}
.digits .digits-num .dot4 {
animation: cursor1 1.2s infinite 0.3s;
}
.digits .digits-num .dot5 {
animation: cursor1 1.2s infinite 0.4s;
}
.digits .digits-num .dot6 {
animation: cursor1 1.2s infinite 0.5s;
}
.digits .digits-num.error p {
border-color: red;
color: red;
}
// 模拟光效效果
.digits .active::after {
content: '';
position: absolute;
width: 1px;
height: 20px;
background-color: pink;
left: 0;
right: 0;
top: 0;
bottom: 0;
margin: auto;
animation: cursor 0.8s infinite;
}
.digits .error-tips {
margin-top: 8px;
color: red;
font-size: 12px;
line-height: 140%;
}
@keyframes cursor1 {
0% {
opacity: 0.1;
}
50% {
opacity: 0.8;
}
100% {
opacity: 0.1;
}
}
2.1.2 html
<template>
<div class="digits">
<input :maxlength="6" type="number" v-model.number="verifyCode" ref="ndVerifyCode" @input="handleInputVerifycode" @focus="handleFocusVerifyCode" @blur="handleBlurVerifyCode" @paste="handlePasteVerifyCode">
<div class="mui-fl-vert digits-num" :class="{'error': !verifyCodeValid}">
<template v-if="!verifyCodeLoading">
<p v-for="(item, index) of 6" :key="index" :class="{'mui-fl-central': 1, 'active': verifyCodeActiveIndex === index}" @click="handleClkPreCode()">{{ verifyCode[index] }}</p>
</template>
<template v-else>
<p v-for="i of 6" :key="i" class="mui-fl-central dots"><span :class="`dot${i}`"></span></p>
</template>
</div>
<p v-show="!verifyCodeValid" class="error-tips">Verification code incorrect. Please try again.</p>
</div>
</template>
2.1.3 JS
<script setup lang="ts">
const verifyCode = ref<string>('')
const ndVerifyCode = shallowRef<HTMLInputElement>()
const verifyCodeActiveIndex = ref() // 光标位置
const verifyCodeValid = ref<boolean>(true) // 验证码或者粘贴内容是否合法
const verifyCodeLoading = ref<boolean>(false)
onMounted(() => {
// 在app内,自动弹起键盘,android可以,ios由于有安全机制,需要用户手动点击触发
ndVerifyCode.value?.focus()
})
const verifyLoginQrCode = () => {
// 校验验证码的逻辑
}
const handleInputVerifycode = () => {
const inputValue = verifyCode.value.toString().replace(/\s/g, '')
verifyCode.value = inputValue.toString().replace(/[^\d]/g, '')
verifyCodeValid.value = true
verifyCodeActiveIndex.value = verifyCode.value.toString().length
if (verifyCode.value.toString().length === 6) {
verifyLoginQrCode()
}
}
const handleFocusVerifyCode = () => {
verifyCodeActiveIndex.value = verifyCode.value.length
}
const handleClkPreCode = () => {
ndVerifyCode.value?.focus()
}
const handleBlurVerifyCode = () => {
verifyCodeActiveIndex.value = ''
}
const handlePasteVerifyCode = (event: any) => {
event.preventDefault();
const clipboardData = event.clipboardData
const pastedText = clipboardData.getData('text').toString().replace(/\s/g, '')
if (!/^\d*$/.test(pastedText) || pastedText.length !== 6) {
verifyCodeValid.value = false
// 因为input type=number 时,粘贴不合法的内容时,value为'',但是input框上面还显示转译后的内容,导致用户再次输入后,无法正常显示新的验证码,所以使用了先赋值,在使用setTimeout使值为空
verifyCode.value = '0'
setTimeout(() => {
verifyCode.value = ''
verifyCodeActiveIndex.value = 0
})
} else {
verifyCode.value = pastedText
verifyLoginQrCode()
}
}
</script>
2.2 # 关键点
2.2.1. input type=number 时,处理粘贴逻辑
2.2.2. 清除获取不到input的value值
const handlePasteVerifyCode = (event: any) => {
event.preventDefault();
const clipboardData = event.clipboardData
const pastedText = clipboardData.getData('text').toString().replace(/\s/g, '')
if (!/^\d*$/.test(pastedText) || pastedText.length !== 6) {
verifyCodeValid.value = false
verifyCode.value = '0'
setTimeout(() => {
verifyCode.value = ''
verifyCodeActiveIndex.value = 0
})
} else {
verifyCode.value = pastedText
verifyLoginQrCode()
}
}