前言
本篇主要分享如何实现一个弹窗展示形式的6位卡号输入功能。
6位卡号输入
前面是根据卡的不同状态的流程实现,接下来,讲讲卡号输入的交互实现。
卡号输入 UI
UI 的呈现,会影响前端的实现方式。这里 UI 设计成弹出层的方式,每个数字都是一个方框。
开发前
在开发前,我列了一些可能出现的问题。比如,不同手机的光标样式、输入时页面抖动、input无法只输入数字等。
所以我在实现的时候,有针对性的规避前面罗列的问题。
功能设计
功能点
首先我罗列了一下主要的功能点,包含了前面提到的问题的规避:
1、唤起手机数字软键盘,只允许输入数字
2、卡号限制6位
3、可删除,删除后光标自动前移,直到第一位
4、重置光标样式
光标样式是浏览器自带样式,考虑重置生效的兼容性和不同浏览器的兼容性
5、光标自动后移
单一的input输入框设计,防止页面抖动
实现
1、唤起手机数字软键盘,只允许输入数字
一般我们将 input 输入框设置为 type='number',此时便只能输入数字了。
但是这个设置在IOS下是不起作用的,即便我又加了pattern="[0-9]*",也还是能输入e、+、-等符号,这个时候,就只需要上正则了。
正则判断数字类型,如果不满足,则将输入值重置为空。
let value = e.target.value;
let regExp = /^[0-9]+$/;
if (!regExp.test(value)) {
value = '';
}
2、卡号限制6位,光标自动后移
输入框设置为 type='number,maxLength设置值是不会生效的。(如果我没记错的话,只有type='tel',会生效)
所以光标移到最后,如果不做特殊处理,输入框可以一直输入值。
于是我将最后的值做了截取,只保留前6位。
// 卡号数组
let [carmiList, setCarmiList] = useState([]);
// 光标所在位置
let [currIndex, setCurrIndex] = useState(1);
// 只允许输入单个数字
if (value > 10) {
value = value.slice(0, 1);
}
// 收集6位卡号
if (currIndex <= 6) {
carmiList.push(value);
}
// 当光标到达第6个方框时,卡号只取前面6位
if (currIndex >= 6) {
nativeInputRef.current.value = value;
carmiList = carmiList.slice(0, 6);
setCarmiList([...carmiList]);
return;
}
3、删除操作
通过监听删除键,处理删除功能。keyCode 的值为8时,表示点击了删除键。
如果此时光标在第一个方框,则清空全部卡号。
如果光标不在第一位,则将卡号数组移除一位,得到新的卡号值,同时光标减去1,得到新的光标位置。
const isBackSpaceKey = e.keyCode === 8;
if (isBackSpaceKey) {
if (currIndex <= 1) {
setCarmiList([]);
setCurrIndex(1);
} else {
carmiList.pop();
let newValue = carmiList.slice();
setCarmiList([...newValue]);
setCurrIndex(currIndex - 1);
}
}
4、重置光标样式
caret-color:设置 input 元素中光标的颜色。
::first-line:用来指定选择器第一行的样式。caret-color 无法改变 IOS 的光标颜色,所以又设置了 ::first-line 选择器的颜色用来兼容 IOS。
input {
caret-color: goldenrod;
outline: none;
&:focus{
border-color: goldenrod;
}
// 兼容Safari
&::first-line {
color: goldenrod;
}
}
5、光标自动后移
为了防止抖动,所以我只保留了一个 input 输入框,通过光标位置更换 input 输入框展示的位置。
<div className='card'>
{carmiList.map((item, index) => {
return (
<Fragment key={index}>
{currIndex === index ? (
<input
className='card-item'
ref={nativeInputRef}
key={index}
id='carmiEle'
type='number'
pattern='[0-9]*'
autoFocus
autoComplete='off' // 不保存输入的数字
maxLength='1'
onChange={e => handleChange(e, index)}
onKeyDown={e => handleDelete(e, index, item)}
inputMode='numeric'
/>
) : (
<div className='card-item'>{item.num}</div>
)}
</Fragment>
);
})}
</div>
躲不开的兼容性问题
测试过程,很顺利,直到 iPhone 12及以上,页面抖动的效果,「出人不意料之外」的出现了。真是令前端「闻风丧胆战心惊」的移动端兼容性。
在移动端兴起以前,PC端的兼容性处理,是最令前端头疼的事情之一。但是PC端的兼容性问题,还是比较好复现的,即便开发自己的电脑没有对应的浏览器,身边人也能找到,再不济,装个虚拟机,也能满足。
但是移动端的兼容性,排查起来,就没那么容易了,手机机型的匮乏,是一个问题。另一个问题则是,移动端的环境不像PC端那么直观。
无论问题有多难,还是要解决它。
曲折的问题解决过程
这个抖动,我最开始觉得是因为页面有滚动条,所以每次输入数字之后,input失焦,又很快再次聚焦,导致页面发生了快速的滚动。所以出现了抖动。
所以开始的时候我就想方设法的不让页面产生滚动。
可能方案1
打开卡号输入弹出层的同时,将页面滚动到顶部。
/**
* 快速定位到顶部
*/
const goTop = () => {
document.body.scrollTop = document.documentElement.scrollTop = 0;
};
结果,该抖还是抖。
可能方案2
打开弹出层的时候,将页面设置为禁止滚动。
document.body.style.overflow = 'hidden';
结果,该抖还是抖。
可能方案3
所以说禁止页面滚动的方式无法解决问题,连暂时方案都算不上。最根本的还是解决 input 聚焦和失焦导致页面发生抖动的情况。
如果 input 一直不失焦不就行了?我感觉我可能找到了真正的解决方案。
不失焦就表示它一直是输入状态,也就是它不是被替换了而是发生了挪动,也就是说它挪到了下一个方框上。
input 输入框上设置好可移动属性,X轴的值是动态的。
style={{ transform: `translate(${cardX}px, -32px)` }}
输入操作,每次向右移动一个方框+边距的距离;
删除操作,每次向左移动一个方框+边距的距离;
// X轴移动的距离
const [cardX, setCardX] = useState(0);
// 每次移动的距离
let [drift, setDrift] = useState(0);
// 输入操作
setCardX(cardX + drift);
// 删除操作
setCardX(cardX - drift);
每次移动的距离,我是算出来的,因为不同手机下的值不一样。
/**
* 获取每次移动的位移
*/
const getDriftVal = () => {
let contentItem = document.getElementsByClassName('content-item')[0];
let contentClass = document.getElementsByClassName('card-content')[0];
let itemOffsetWidth = contentItem.offsetWidth;
let boxOffsetWidth = contentClass.offsetWidth;
let widthDVal = boxOffsetWidth - itemOffsetWidth * 6;
let interval = Filter.accuracyExcept(widthDVal, 5);
drift = Filter.accuracyAdd(interval, itemOffsetWidth) || 56;
setDrift(drift);
};
到这,抖动的问题,终于被解决了。
总结
想要解决问题,关键还是要找到问题的本质,对症下药。
但是有时候,问题的根本原因并不是那么好找到,这需要我们日常多积累经验+不断的学习巩固。
6位卡号输入功能,该功能可能出现的兼容性问题,以及解决这些问题的思路和方案。
兼容性问题,对于前端而言,是一个「拦路虎」。能找到问题的本质,有时候并不容易,但是每一次的寻找答案的过程,都会有所收获。
本次分享的iOS系统input快速聚焦和失焦导致页面抖动的解决方式,希望能帮到每一个有需要的开发者。共勉。
作者:非职业「传道授业解惑」的开发者叶一一简介:「趣学前端」、「CSS畅想」系列作者,华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。如果看完文章有所收获,欢迎点赞
标签:弹窗,实用功能,value,let,输入,卡号,input,光标 From: https://blog.51cto.com/u_15838863/8676333