H5实现支付密码输入框(vue3)

微信、支付宝支付密码时的密码输入框大家都很熟悉,它由6个小格子组成,输入一个密码后会自动跳转到另一个格子,密码输入完成后就可以提交数据,在web并没有原生的这种输入框,要想使用这种输入框需要自己去实现。

最近的一个项目中需要使用这种输入框,由于项目比较小,因此就没有用其他的UI库,于是就自己造了一个密码输入框的轮子,效果如下图:

安卓设备:
H5实现支付密码输入框(vue3)_第1张图片

iOS设备:

在ios上有一个小小的问题:输入框获得焦点后无法自动弹窗软键盘,有大神知道解决方法的话请评论区留言告知!!

1、DOM结构

dom结构这块比较简单,主要由:

  1. 隐藏的input
    主要作用是:调起系统软键盘以及输入内容
  2. 6个小格子
  3. 虚拟光标

3部分组成。

2、显示密码格子

界面上的密码格子数量主要通过父组件传递的密码及密码长度来计算

let passwords = computed(function () {
    let value = props.modelValue;
    if (typeof value !== 'string' && typeof value !== 'number') {
      value = '';
    } else {
      value = value + '';
    }

    // console.log('value', value);
    let resultArr = value.split('');
    let len = props.length; // 密码长度限制,默认为6位
    let diff = value.length - props.length;
    if (diff > 0) {
      resultArr = value.substr(0, len).split('');
    } else if (diff < 0) {
      diff = Math.abs(diff);
      // 如果传递的modelValue长度小于密码长度,则填补补空格,以达到界面上密码格子数量等于密码长度
      while (diff > 0) {
        resultArr.push(' ');
        diff--;
      }
    }

    return resultArr;
  });

3、计算获得焦点的格子的索引

当在输入时、删除时、用户点击格子时都需要知道当前哪个格子应该获得焦点,格子获得焦点后虚拟光标才会显示出来。如果calcFocusInputIndex()执行后的值为-1则说明密码输入完成,此时没人任何格子需要获得焦点

let calcFocusInputIndex = function () {
  let pwdVal = passwords.value;
  let index = -1;
  let realPwdVal = trim(pwdVal.join(''));
  console.log('realPwdVal', realPwdVal, realPwdVal.length, pwdVal);
  for (let i = 0, len = pwdVal.length; i < len; i++) {
    // pwdVal[i]为空格表示该格子并没有真正的值
    if (pwdVal[i] === ' ' && realPwdVal.length !== props.length) {
      index = i;
      break;
    }
  }
  console.log('index', index);
  return index;
};

4、监听输入

在用户输入密码时如果输入的不是数字,则需清空input里的内容
当用户输入的密码长度等于props.length时立即完成输入

let onInput = function (evt) {
  let inputValue = evt.target.value;
  if (inputValue && !numberReg.test(inputValue)) { // 如果输入的不是数字则清空输入框
    evt.target.value = '';
    return;
  }
  console.log('输入的字符为:', inputValue);
  let password = passwords.value.join('');
  password = trim(password);
  password += inputValue;
  evt.target.value = '';
  ctx.emit('update:modelValue', password);
  if (password.length == props.length) {
    ctx.emit('complete', password);
  }
  // 隐藏输入框焦点
  nextTick(function () {
    let inputIndex = calcFocusInputIndex();
    if (inputIndex == -1) {
      blur();
    } else {
      focusInputIndex.value = inputIndex;
    }
  });
  console.log('更新modelValue', password);
};

5、隐藏掉input的光标

由于格子本身就有虚拟光标,那么此时input的光标就不需要再显示出来了。另外,就算没有虚拟光标也很难将input的光标准确的定位到每一个格子中间。

隐藏掉input光标主要靠3个技巧:

  1. 用绝对定位来隐藏input,而不是通过display: none;隐藏
  2. 字体颜色设为透明,字体阴影设为0
  3. text-indent 设置一个稍微大点的负值(主要解决ios设备中光标隐藏不了问题)
.hidden-input{
  position: absolute;
  top: 5px;
  z-index: 1;
  /* 隐藏光标 start */
  color: transparent;
  text-shadow: 0 0 0 #000;
  /* 隐藏光标 end */

  /* 隐藏ios设备光标 start */
  text-indent: -999em;
  margin-left: -40%;
  /* 隐藏ios设备光标 end */
}

6、完整代码

PasswordInput.vue





password-input.less

.bs-password-input{
  position: relative;
  overflow: hidden;
  .hidden-input{
    position: absolute;
    top: 5px;
    z-index: 1;
    /* 隐藏光标 start */
    color: transparent;
    text-shadow: 0 0 0 #000;
    /* 隐藏光标 end */

    /* 隐藏ios设备光标 start */
    text-indent: -999em;
    margin-left: -40%;
    /* 隐藏ios设备光标 end */
  }
}
.bs-password-input-security{
  position: relative;
  z-index: 5;
  display: flex;
  height: 40px;
  user-select: none;
  background-color: #fff;
}
.bs-password-input-item{
  position: relative;
  z-index: 5;
  display: flex;
  flex: 1;
  justify-content: center;
  align-items: center;
  height: 100%;
  cursor: pointer;
  font-size: 20px;
  background-color: #F2F2F2;
  &:not(:first-child)::before{
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    content: ' ';
    width: 1px;/*no*/
    background-color: #ececec;
  }
  &.is-focus{
    .password-input-dot{
      visibility: hidden;
    }
    .bs-password-input-cursor{
      display: block;
    }
  }
}
.password-input-dot{
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background-color: #000;
}
.bs-password-input-cursor{
  display: none;
  position: absolute;
  top: 50%;
  left: 50%;
  width: 1px;/*no*/
  height: 40%;
  transform: translate(-50%, -50%);
  cursor: pointer;
  background-color: rgba(32,32,32,3);
  animation: 1s cursor-flicker infinite;
}

.bs-password-input-security{
  &.has-gap{
    .bs-password-input-item{
      border-radius: 4px;
      &::before{
        display: none;
      }
      &:not(:first-child){
        margin-left: 15px;
      }
    }
  }
}
.bs-password-input-info {
  margin-top: 15px;
  color: #999;
  text-align: center;
}


@keyframes cursor-flicker {
  0% {
    opacity: 0;
  }
  50% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

使用



你可能感兴趣的:(H5实现支付密码输入框(vue3))