如何实现一个新拟物风格的验证码组件

102 阅读1分钟

最近看到新拟物风格的倾向,于是手痒难耐,决定尝试实现一个新拟物风格的验证码组件。

实现思路

  1. 上面是一个显示输入验证码的列表
  2. 底部是一个不可见的输入框
  3. 点击输入位置,将不可见的输入框激活

直接上干货

<template>
  <view class="yuumi-captcha" @longtap="onPaste" @tap="onPasted">
    <view class="captcha-box">
      <view v-for="item in maxlength" :key="item" :style="itemStyle"
        :class="['captcha-item', { _cursor: focused && item === captcha.length}]"
        @tap="autoFocus"
      >
        {{ captcha.slice(item, item + 1) }}
      </view>
    </view>

    <view class="ghost">
      <input :type="type" :maxlength="maxlength" v-model="captcha" :focus="focused" @blur="onBlur">
    </view>
  </view>
  </template>

  <script lang="ts">
  import Vue from 'vue'

  export default Vue.extend({
    props: {
      // 验证码位数
      maxlength: { type: Number, default: 6 },
      // 单个输入框的尺寸
      size: { type: String,  default: '80rpx' },
      // 输入框的类型
      type: { type: String, default: 'number' },
      // 是否启用长按粘贴
      pasteEnable: { type: Boolean }
    },
    data () {
      return {
        captcha: "",
        focused: false,
        pasting: false
      }
    },
    computed: {
      itemStyle(): string{
        return `flex-basis: ${this.size};height: ${this.size};line-height: ${this.size};`
      }
    },
    methods: {
      autoFocus() {
        if (this.focused || this.pasting) return
        this.focused = true
      },
      onBlur() {
        this.focused = false
        this.completed()
      },
      onPaste() {
        if (!this.pasteEnable) return

        this.pasting = true
        uni.getClipboardData({
          success: (res: any) => {
            this.captcha = (res.data || "").slice(0, 6)
          }
        })
      },
      onPasted() {
        if (!this.pasting) return

        this.$nextTick(() => {
          this.pasting = false
          this.completed()
        })
      },
      completed() {
        if (this.captcha.length === this.maxlength) {
          this.$emit("complete", { value: this.captcha })
        }
      }
    }
  })
  </script>

  <style lang="scss" scoped>
    .captcha-box {
    display: flex;
    align-items: center;
    justify-content: space-around;
    .captcha-item {
      position: relative;
      flex: 0 0 auto;
      text-align: center;
      color: #50a684; 
      background-color: #eaecf1;
      border-radius: 8rpx;
      box-shadow:
        -6rpx -6rpx 18rpx rgba(255, 255, 255, 1),
        6rpx 6rpx 18rpx rgba(0, 0, 0, 0.2);
      transition: boxShadow 0.3s;

      &._cursor::after {
        display: block;
        content: "";
        position: absolute;
        top: 50%;
        left: 50%;
        width: 2px;
        height: 16px;
        transform: translate3d(-50%, -50%, 0);
        background-color: #50a684;
        animation: cursor 1s linear infinite;
      }

      &._cursor {
        box-shadow:
          -6rpx -6rpx 18rpx rgba(255, 255, 255, 1) inset,
          6rpx 6rpx 18rpx rgba(0, 0, 0, 0.2) inset;
      }
    }
  }

  .ghost {
    width: 0;
    height: 0;
    overflow: hidden;
  }

  @keyframes cursor {
    0% {
      background-color: transparent;
    }

    50% {
      background-color: #50a684;
    }

    100% {
      background-color: transparent;
    }
  }
  </style>

记得将页面背景色设置为 #eaecf1 这样效果才能出来

后记(欢迎扫码体验)

gh_6857aea296db_344 (1).jpg