最近在用uni-app开发时遇到一个相似微信领取的明码框需要,要求:用户输出明码后主动向后跳转一个输入框,并且取得焦点,直到输出结束。用户删除时,删除完以后输入框的内容,再按一个“退格/删除”键,则主动往前跳一个输入框,并将其内容删除。成果如:
实现思路有且只能有一个input输入框 如果采纳一个方框用一个input输入框,在模拟器里没有什么问题,但在实在的手机中会呈现软件盘弹起不了问题。肉眼看到的输入框(方框)是虚构的,光标也是虚构的input输入框的大小与方框大小统一,字体大小保持一致,字体色彩为通明,设置字体色彩为通明后输入框的光标也会随之隐没光标挪动到哪个方框,input输入框也要随之挪动input输入框限度最多只能输出2个字符,如果只有一个字符则要在该字符后面补一个空格 因为要实现以后输入框的值删除掉后再按一个“退格/删除”键以后输入框的前一个输入框的值也要删除掉性能用户在方框中输出一个字符后,input输入框立刻挪动到下一个方框,并且清空input输入框删除行为,在input输入框中应用input事件来模仿“退格/删除”行为代码实现template
<template> <view class="password-input-com" ref="passwordInputCom"> <input ref="passwordInput" v-model="inputValue" :focus="inputFocus" :style="{left: passwordInputLeft + 'px'}" type="text" maxlength="2" @input="onInput" @blur="onBlur" class="password-input"> <view class="virtual-input-list" ref="virtualInputList"> <view class="virtual-input-item" v-for="(item, index) in virtualInputs" :key="index" :class="{security: mask, 'input-focus': virtualInputItemIndex == index}" @click="onVirtualInputClick(index)"> <view v-if="!mask" class="text-viewer">{{item.value}}</view> <view v-show="item.value != ' ' && mask" class="security-mask"></view> <view class="virtual-input-cursor"></view> </view> </view> </view></template>javascript
<script> export default { name: "PasswordInput", props: { value: { type: String, default: '' }, length: { // 明码最大长度 type: Number, default: 6 }, mask: { type: Boolean, default: false } }, data() { // 获取运行平台 let getPlatform = () => { let platform; // #ifdef H5 platform = 'H5'; // #endif // #ifdef MP-WEIXIN platform = 'mp-weixin'; // #endif // #ifdef MP-ALIPAY platform = 'mp-alipay'; // #endif return platform; } return { platform: getPlatform(), virtualInputs: [], // specialStr: '●', // 特殊字符 // splitStr: '★', // 宰割字符 inputValue: '', inputFocus: false, passwordInputLeft: 1, virtualInputItemIndex: -1, passwordInputComRect: { width: 0, height: 0, left: 0, right: 0 }, virtualInputItemRect: { width: 0, height: 0, left: 0, right: 0 } }; }, watch: { value: { immediate: true, handler(newVal){ this.calcVirtualInputs(newVal); } } }, methods: { // 计算须要输入框的个数 calcVirtualInputs(newVal){ let valueArr = ((newVal + '').length > 0 ? (newVal + '') : '●●●●●●●●●●●●●●●●●●●●●●●●').split(''); let length = this.length; // console.log('valueArr', valueArr) if(valueArr.length > length){ valueArr.splice(length); }else if(valueArr.length < length){ let lengthDiff = length - valueArr.length; while(lengthDiff > 0){ valueArr.push('●'); lengthDiff--; } } let virtualInputs = valueArr.map((str, index) => { return { value: str == '●' ? ' ' : str, focus: false, index: index }; }); this.virtualInputs = virtualInputs; }, onInput(evt){ // console.log(evt) let val = evt.detail.value; let virtualInputItemIndex = this.virtualInputItemIndex; console.log('onInput', val); if(val.length == 2){ // 以后虚构输入框输出值后立刻向后一个输入框挪动 this.virtualInputs[virtualInputItemIndex].value = val.charAt(1); if((virtualInputItemIndex + 1) < this.length){ this.virtualInputItemIndex = virtualInputItemIndex + 1; this.inputMoveTo(this.virtualInputItemIndex, () => { let nextVirtualInputVal = this.virtualInputs[this.virtualInputItemIndex].value; console.log('nextVirtualInputVal', nextVirtualInputVal) // 这里须要提早60毫秒再设置下一个虚构输入框的值,不然有效 let timer = setTimeout(() => { clearTimeout(timer); this.inputValue = nextVirtualInputVal == ' ' ? nextVirtualInputVal : (' ' + nextVirtualInputVal); console.log('this.inputValue', this.inputValue) }, 60); }); } this.$nextTick(() => { this.detectInputComplete(); }); } else if(val.length == 1){ console.log('length等于1', val) if(val == ' '){ // 以后操作为删除虚构框中的值 this.virtualInputs[virtualInputItemIndex].value = ' '; }else{ // 以后操作为正在输出 if((virtualInputItemIndex + 1) < this.length){ this.virtualInputItemIndex = virtualInputItemIndex + 1; this.inputMoveTo(this.virtualInputItemIndex, () => { let nextVirtualInputVal = this.virtualInputs[this.virtualInputItemIndex].value; let timer = setTimeout(() => { clearTimeout(timer); this.inputValue = nextVirtualInputVal == ' ' ? nextVirtualInputVal : (' ' + nextVirtualInputVal); console.log('this.inputValue2', this.inputValue) }, 60); }); } this.$nextTick(() => { this.detectInputComplete(); }); } } else if(val.length == 0){ // 往前一个输入框挪动,并删除其值 if(virtualInputItemIndex - 1 >= 0){ this.virtualInputItemIndex = virtualInputItemIndex - 1; this.inputMoveTo(this.virtualInputItemIndex, () => { this.virtualInputs[this.virtualInputItemIndex].value = ' '; // 这里须要提早60毫秒再设置下一个虚构输入框的值,不然有效 let timer = setTimeout(() => { clearTimeout(timer); this.inputValue = ' '; }, 60); }); } } }, onBlur(){ this.inputFocus = false; this.virtualInputItemIndex = -1; }, detectInputComplete(){ let length = this.length; let valStr = this.getValue(); console.log('detectInputComplete', valStr); if(length == valStr.length){ this.$emit('complete', valStr); } }, inputMoveTo(virtualInputIndex, cb){ console.log('inputMoveTo', virtualInputIndex) let passwordInputComRect = this.passwordInputComRect; let obj = uni.createSelectorQuery().in(this).selectAll('.virtual-input-item'); // 获取元素宽高 obj.boundingClientRect((rectData) => { console.log(rectData) let currentDomRect = rectData[virtualInputIndex]; console.log('currentDomRect', currentDomRect, virtualInputIndex, passwordInputComRect) // +1是因为有1px的左边框 this.passwordInputLeft = currentDomRect.left - passwordInputComRect.left + 1; typeof cb == 'function' ? cb() : 1; }).exec(); }, onVirtualInputClick(index){ console.log('onVirtualInputClick', index) let $passwordInput = this.$refs.passwordInput; this.inputMoveTo(index, () => { let virtualInputVal = this.virtualInputs[index].value; this.inputFocus = true; this.inputValue = virtualInputVal == ' ' ? virtualInputVal : (' ' + virtualInputVal); this.virtualInputItemIndex = index; if(this.platform == 'H5'){ this.$refs.passwordInput.$el.focus(); } }); }, getValue(){ let length = this.length; let valStr = this.virtualInputs.reduce((res, item) => { let itemVal = item.value.replace(/ /g, ''); return res += itemVal; }, ''); if(valStr.length > length){ valStr = valStr.substr(0, length); } return valStr; } }, mounted() { this.$nextTick(() => { let obj = uni.createSelectorQuery().in(this).select('.password-input-com'); // 获取元素宽高 obj.boundingClientRect((data) => { if(!data){ // 支付宝小程序获取不到地位信息 let systemInfo = uni.getSystemInfoSync(); let wh = systemInfo.windowWidth; let rpxCalcIncludeWidth = 750; let pagePaddingLeft = 48; data = { left: wh / 750 * 48 } }else{ this.passwordInputComRect = data; } console.log('组件宽高地位信息', data) }).exec(); }); } }</script>css
...