实用功能分享,弹窗展示形式的6位卡号输入功能实现【玩转业务功能】

举报
叶一一 发表于 2023/08/26 09:35:36 2023/08/26
【摘要】 前言本篇主要分享如何实现一个弹窗展示形式的6位卡号输入功能。6位卡号输入前面是根据卡的不同状态的流程实现,接下来,讲讲卡号输入的交互实现。卡号输入 UIUI 的呈现,会影响前端的实现方式。这里 UI 设计成弹出层的方式,每个数字都是一个方框。开发前在开发前,我列了一些可能出现的问题。比如,不同手机的光标样式、输入时页面抖动、input无法只输入数字等。所以我在实现的时候,有针对性的规避前面罗...

前言

本篇主要分享如何实现一个弹窗展示形式的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畅想」系列作者,华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。