The effect

Let’s take a look at the finished effect of the wave

Preview the address

Making the address

NPM address

demand

Enter a 4-digit or 6-digit SMS verification code, and then fold up the keyboard

Implementation steps

The first step

The layout and typesetting

<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>
Copy the code
  • Use the li element to simulate the display of the input field for no other purpose than semantics, but you can also use any other element, such as div.
  • The advantage of using the label tag is that it can be associated with the input click event, on the one hand implementing a semantic solution and on the other hand saving us from invoking the virtual keyboard through JS.

Hide input field

.input-code {
    position: absolute;
    left: -9999px;
    top: -9999px;
}
Copy the code
  • By positioning the real input block outside of the visible area of the screen, the virtual keyboard does not push the page upwards when aroused. So your captcha input component must be in a place where the virtual keyboard can’t hide it.

The second step

Process captcha input

handleSubmit () {
  this.$emit('input', this.value)
},
handleInput (e) {
  if (e.target.value.length >= this.length) {
    this.hideKeyboard()
  }
  this.handleSubmit()
}
Copy the code
  • During input, the input field is assigned a value to solve the problem of refocusing the input field after it is out of focus on the Android terminal. The input cursor will be fixed in front of the first digit, and the cursor position will be displayed behind the last digit after the assignment and refocusing.

The third step

Close the virtual keyboard after typing

hideKeyboard() {/ / input complete hidden keyboard document. ActiveElement. The blur () / / ios hide this keyboard.$refs.input.blur() // Android hide keyboard}Copy the code

Component complete code

<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: The '-'
      },
      theme: {
        type: String,
        default: 'block'
      }
    },
    // variables
    data () {
      return {
        value: ' '
      }
    },
    computed: {
      uuid () {
        return Math.random().toString(36).substring(3, 8)
      }
    },
    methods: {
      hideKeyboard() {/ / input complete hidden keyboard document. ActiveElement. The blur () / / ios hide this keyboard.$refs.input.blur() // Android hide keyboard},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>

Copy the code

Component usage code

<security-code v-model="code"></security-code>
Copy the code

conclusion

How about 484 so easy

The original idea was to have four input fields and listen for input to complete and jump to the next one. This would have worked, but it would have required more code to maintain this rule, which was not elegant.

The current approach has been the best solution I can think of, if you have a better idea of implementation, please kindly advise.

Preview the address

Making the address

NPM address