如何实现一个顺滑的输入框

在做微信小程序开发中,遇到一个类似于输入密码这样的一对独立的输入框,发现这个输入框要做的丝滑还是需要花一些心思的,
如下图所示

最开始的想法很简单,就是一个数字框一个input搞定,这样实现,大体上是没什么毛病的,不过有两个缺点:

    1. 每个输入框切换的时候键盘会收起又展开,感觉不流畅,
    1. 当我连续输入的时候是很难控制把输入的数字挨个填进对应的输入框的。

于是就开始思考,能不能像输入密码框那样我们能够连续而且丝滑的输入
不过这个输入框还是和密码框有几个交互上的不一样:

  1. 密码框一般就是从前向后输入,而这个数字框则有可能从前向后,也有可能从后向前
  2. 密码框不会出现从中间输入,数字框则有可能从任意位置向后或者前输入。

解决输入框焦点切换导致的键盘收起和展开

解决切换input输入框的时候焦点切换导致的键盘收起和展开其实就一个办法,只使用一个输入框,不触发失焦就好了,
那么我们可以把这几个数字输入框换成一个输入框,然后设置letter-space属性增大字体的间距。
但是实现以后发现整体效果并不好,原因有两个:

    1. 小程序里面这个属性并不生效,猜测应该是原生组件的原因导致,
    1. 另外一个就是无法控制输入的位置,连续输入会导致后面的输入挤占了前面输入的数字

不过键盘这个切换的bug是没有了

李代桃僵-换个思路

上面的思路不通,那就只好换一个思路,新的思路就是有一个离屏的输入框,来记录用户的输入,在界面上还是绘制对应的数字输入框,但是他只作为展示使用,
我们通过用户点击的输入框的序号,来记录对应的输入内容。具体代码如下

// 这是展示数来的数字输入框
 <text class="number-input" 
  type="number" wx:key="{{this}}"
   data-cursor="{{index===inputIndex}}"
    bind:tap="textClick" 
    wx:for="{{inputArray}}" 
    wx:for-item="num" 
    data-index="{{index}}">
    {{num.value}}
    </text>
// 这是实际的离屏的输入框
     <input name="topicInputValue" 
     selection-start="{{inputIndex}}" 
     selection-end="{{inputIndex+1}}" 
     focus="{{focusIndex}}" 
     bindinput="bindNumberInput" 
     value="{{inputValue}}"
    class="input-shadow"
     type="number"/>

     
.input-shadow{
  position: fixed;
  top:-100000px;
  left: -100000px;
  opacity: 0;
}

实际运行时候的js

  textClick(event) {
    const index = Number(event.currentTarget.dataset.index);
    this.inputIndex = index;
    // 判断点击位置,前置的数据填充
    const inputValue = this.fillInputValueByIndex(index);
    this.setData({ focusIndex: true, inputIndex: index, inputValue })
  },
  fillInputValueByIndex(index) {
    const { inputArray } = this.data;
    let v = "";
    let m = index;
    while (m > -1) {
      let val = inputArray[m].value;
      const t = val !== undefined || val !== "" ? val : " ";
      v = `${t}${v}`;
      m--;
    };
    m = index + 1;
    while (m < inputArray.length) {
      v = `${v}${inputArray[m].value}`;
      m++;
    }
    return v;
  },
bindNumberInput(event) {
    const { value, keyCode } = event.detail;
    console.log("event.detail===", event.detail);
    const { inputArray, inputIndex } = this.data;
    if (keyCodeMap.get(keyCode) === 'del') {
      // 删除前值
      inputArray[inputIndex].value = "";
      this.inputIndex = this.inputIndex ? this.inputIndex - 1 : this.inputIndex;
      this.setData({
        inputValue: value,
        inputArray,
        inputIndex: this.inputIndex
      })
      return value;
    };
    this.fillTextByValue(value, keyCode)
  },
  fillTextByValue(value, keyCode) {
    const { inputArray } = this.data;
    if (keyCodeMap.has(keyCode)) {
      console.log("=====", keyCodeMap.get(keyCode));
      if (this.inputIndex < inputArray.length) {
        inputArray[this.inputIndex].value = keyCodeMap.get(keyCode);
      }
      this.inputIndex = this.inputIndex < inputArray.length - 1 ? this.inputIndex + 1 : this.inputIndex;
    }
    this.setData({
      inputValue: value,
      inputArray,
      inputIndex: this.inputIndex
    })
  },

 const keyCodeMap = new Map(
  [[8, "del"],
  [46, "del"],
  [48, 0],
  [49, 1],
  [50, 2],
  [51, 3],
  [52, 4],
  [53, 5],
  [54, 6],
  [55, 7],
  [56, 8],
  [57, 9]]
)

代码的逻辑如下:

  • 1.根据用户点击的输入框位置来设置对应的 inputIndex,并且把之前数字输入框的值填入到真实的输入框中,这里需要判断和区分的是首尾有输入,中间是空值的情况,
  • 2.根据用户输入的keyCode 来映射对应的数字,这里需要判断用户是否点击了删除键。
  • 3.若用户输入的为数字,则获取对应的数字填充到数字框,同时 inputIndex值+1
  • 4.用户连续输入则顺序添加对应的数字,注意需要根据对约的输入框截断
  • 5.若用户输入的是 删除 键则需要删除对应的数字,并且inputIndex的值-1;

继续优化

实际上我们在填这类数字框的时候大致是分两种情况

    1. 加法与乘法 一般是从地位向高位(右向左<–)
    1. 减法与除法 一般是从高位向地位(左向右–>)
    1. 但是也有例外情况 对应一些简单的可能直接算出答案,这样用户就直接从高位向低位填。

所有在连续输入的时候这个inputIndex 不应该是简单的+1 ,应该有个优先级的判断。

本文作者:番茄炒蛋
本文地址: https://www.noway.pub/2023/12/27/one-input/
版权声明:转载请注明出处!