194

使用VUE分分钟写一个验证码输入组件

 6 years ago
source link: https://juejin.im/post/5a31ddb251882527541053ee
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

使用VUE分分钟写一个验证码输入组件 - 掘金

使用VUE分分钟写一个验证码输入组件

2017年12月14日 06:49 ·  阅读 14160
使用VUE分分钟写一个验证码输入组件

先来看波完成效果图

效果图

预览地址

github地址

npm地址

输入4位或6位短信验证码,输入完成后收起键盘

布局排版

<div class="security-code-wrap">
    <label for="code">
      <ul class="security-code-container">
        <li class="field-wrap" v-for="(item, index) in number" :key="index">
          <i class="char-field">{{value[index] || placeholder}}</i>
        </li>
      </ul>
    </label>
    <input ref="input" class="input-code" @keyup="handleInput($event)" v-model="value"
           id="code" name="code" type="tel" :maxlength="number"
           autocorrect="off" autocomplete="off" autocapitalize="off">
</div>
复制代码
  • 使用li元素来模拟输入框的显示,没有别的目的,就只是为了语义化,当然你也可以使用其他任意一个元素来模拟,比如div。
  • 使用label标签的好处在于它可以跟input的click事件关联上,一方面实现了语义化解决方案,另一方面也省去了我们通过js来唤起虚拟键盘。

隐藏输入框

.input-code {
    position: absolute;
    left: -9999px;
    top: -9999px;
}
复制代码
  • 将真实的输入框定位到屏幕可视区域以外的地方,虚拟键盘被唤起时,就不会将页面往上顶了。所以你的验证码输入组件一定要放在虚拟键盘遮挡不了的地方。

处理验证码输入

handleSubmit () {
  this.$emit('input', this.value)
},
handleInput (e) {
  if (e.target.value.length >= this.length) {
    this.hideKeyboard()
  }
  this.handleSubmit()
}
复制代码
  • 输入时,给输入框赋一次值,是为了解决android端上输入框失焦后重新聚焦,输入光标会定在第一位的前面,经过赋值再聚焦,光标的位置就会显示在最后一位后面。

完成输入后关闭虚拟键盘

hideKeyboard() {
  // 输入完成隐藏键盘
  document.activeElement.blur() // ios隐藏键盘
  this.$refs.input.blur() // android隐藏键盘
}
复制代码

组件完整代码

<template>
  <div class="security-code-wrap">
    <label :for="`code-${uuid}`">
      <ul :class="`${theme}-container security-code-container`">
        <li class="field-wrap" v-for="(item, index) in length" :key="index">
          <i class="char-field">{{value[index] || placeholder}}</i>
        </li>
      </ul>
    </label>
    <input ref="input" class="input-code" @keyup="handleInput($event)" v-model="value"
           :id="`code-${uuid}`" :name="`code-${uuid}`" type="tel" :maxlength="length"
           autocorrect="off" autocomplete="off" autocapitalize="off">
  </div>
</template>

<script>
  export default {
    name: 'SecurityCode',
    // component properties
    props: {
      length: {
        type: Number,
        default: 4
      },
      placeholder: {
        type: String,
        default: '-'
      },
      theme: {
        type: String,
        default: 'block'
      }
    },
    // variables
    data () {
      return {
        value: ''
      }
    },
    computed: {
      uuid () {
        return Math.random().toString(36).substring(3, 8)
      }
    },
    methods: {
      hideKeyboard () {
        // 输入完成隐藏键盘
        document.activeElement.blur() // ios隐藏键盘
        this.$refs.input.blur() // android隐藏键盘
      },
      handleSubmit () {
        this.$emit('input', this.value)
      },
      handleInput (e) {
        if (e.target.value.length >= this.length) {
          this.hideKeyboard()
        }
        this.handleSubmit()
      }
    }
  }
</script>

<style scoped lang="less">
  .security-code-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
  }

  .security-code-container {
    margin: 0;
    padding: 0;
    display: flex;
    .field-wrap {
      list-style: none;
      display: block;
      height: 40px;
      width: 40px;
      line-height: 40px;
      font-size: 16px;
      .char-field {
        font-style: normal;
      }
    }
  }

  .block-container {
    justify-content: center;
    .field-wrap {
      background-color: #fff;
      margin: 2px;
      color: #000;
    }
  }

  .line-container {
    position: relative;
    background-color: #fff;
    &:after {
      box-sizing: border-box;
      content: "";
      width: 200%;
      height: 200%;
      transform: scale(.5);
      position: absolute;
      border: 1px solid #d9d9d9;
      top: 0;
      left: 0;
      transform-origin: 0 0;
      border-radius: 4px;
    }
    .field-wrap {
      flex: 1;
      position: relative;
      &:not(:last-child):after {
        content: "";
        width: 1px;
        height: 50%;
        position: absolute;
        right: 0;
        top: 25%;
        background-color: #d9d9d9;
        transform: scaleX(.5);
      }
    }
  }

  .input-code {
    position: absolute;
    left: -9999px;
    top: -9999px;
  }

</style>

复制代码

组件使用代码

<security-code v-model="code"></security-code>
复制代码

怎么样,484 so easy

一开始的思路也是4个输入框,监听输入完成跳到下一个输入框,这样的做法也能达到目的,不过需要更多的代码去维护这个规则,不够优雅。

目前的做法已经是我能想到最优的解决方案,如果你有更好的实现思路,还望不吝赐教。

预览地址

github地址

npm地址


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK