1. Effect drawing

Final effect:

2. Implementation process
The first version
<Flex className="login-code">
    <Flex.Item>
        <InputItem
            type="number"
            className="input1"
            maxLength={1}
            value={codearr[0]}
            onChange={value => {
                console.log("onChange", value)
                codearr[0] = value;
                this.dispatch({ type: "input", data: { codearr: codearr, random: Math.random() } });
                this.moveNext.bind(this, value, 1)();
            }}
        />
    </Flex.Item>
    <Flex.Item>
        <InputItem
            type="number"
            className="input2"
            maxLength={1}
            value={codearr[1]}
            onChange={value => {
                codearr[1] = value;
                this.dispatch({ type: "input", data: { codearr: codearr, random: Math.random() } });
                this.moveNext.bind(this, value, 2)();
            }}
        />
    </Flex.Item>
    <Flex.Item>
        <InputItem
            type="number"
            className="input3"
            maxLength={1}
            value={codearr[2]}
            onChange={value => {
                codearr[2] = value;
                this.dispatch({ type: "input", data: { codearr: codearr, random: Math.random() } });
                this.moveNext.bind(this, value, 3)();
            }}
        />
    </Flex.Item>
    <Flex.Item>
        <InputItem
            type="number"
            className="input4"
            maxLength={1}
            value={codearr[3]}
            onChange={value => {
                codearr[3] = value;
                this.dispatch({ type: "input", data: { codearr: codearr, random: Math.random() } });
                this.moveNext.bind(this, value, 4)();
            }}
        />
    </Flex.Item>
</Flex>
Copy the code
moveNext(value, index) {
    console.log(value, value.length)
    if (value && value.length) {
        setTimeout(() => {// iOS cannot automatically jump $('.input${index + 1} input`).focus();
        })
        this.checkSend();
    } else {
        setTimeout(() => {// iOS cannot automatically jump $('.input${index - 1}input`).focus(); }}})Copy the code

The idea is to create four inputs, jump to the next input and actively focus on the input box.

Disadvantages: when deleting can not achieve smooth backspace delete

The second version
  .key-input {
    width: 100%;
    height: 50px;
    line-height: 50px;
    padding: 0 4px;
    letter-spacing: 49px;
    box-sizing: border-box;
    text-indent: 21px;
    appearance: none;
    border: transparent;
    font-size: 18px;
    color: transparent;
    text-shadow: 0 0 0 #02b8cd;
  }
Copy the code

Use an input field, limit the input field length to 4 and use styles to adjust the spacing between fonts to simulate similar input fields

Disadvantages: When input to the fourth digit, the cursor will continue to jump down but can not input, can not actively control the cursor not jump, after thinking about the fourth digit after actively cancel input focus input, there will still be style problems caused by the cursor.

The third version
import React from "react";

import { Toast, WingBlank } from "antd-mobile";

import { template, ViewComponent, Validators, setEventWithLabel } from "@reco-mobile/core";
import { Namespaces } from "@reco-mobile/auth-models";

import { statisticsEvent } from "@reco-mobile/statistics";

import { Countdownauto } from "./countdownauto"

export namespace Code {
    export interface IProps extends ViewComponent.IProps { }

    exportinterface IState extends ViewComponent.IState { viewRef? : any; }export class Component<P extends IProps = IProps, S extends IState = IState> extends ViewComponent.Base<P, S> {
        showloading = true;
        namespace = Namespaces.login;
        showloadingContent = "Logging in";
        inputRef;
        componentWillUnmount() {
            this.dispatch({ type: "input", data: { codestr: null } });
        }
        componentDidMount() {
            this.dispatch({ type: "hideLoading" });
            this.inputRef.focus()
        }
        tranformClass(index) {
            for (let i = 0; i < 4; i++) {
                if (i + 1 === index) {
                    $(`#cursorspan${i + 1}`).addClass("cursorspan");
                } else{$(`#cursorspan${i + 1}`).removeClass("cursorspan");
                }
            }
        }
        renderHeader(): JSX.Element | null {
            return (
                <>
                    <div className="header-bodernone">{super.renderHeader()}</div>
                </>
            );
        }
        loginNameValidator() {
            const { cellphone, required, composeControl } = Validators,
                { state } = this.props;
            returncomposeControl([required, cellphone], { value: state! .get("loginName"), name: "Mobile phone number" });
        }
        validator() {
            const { required, maxLength, minLength, composeControl, ValidatorControl } = Validators,
                { state } = this.props;

            returnValidatorControl( this.loginNameValidator(), composeControl([required, maxLength(4), minLength(4)], { value: state! .get("password"),
                    name: "Verification code"})); }login() { const msg = this.validator()! (a);if (msg) {
                Toast.fail(msg.join(), 1);
                return;
            }
            this.dispatch({ type: "input", data: { loading: true}}); this.dispatch({type: "submit",
                error: e => Toast.fail(e.errmsg),
                callback: () => {
                    Toast.success("Login successful", 1); }});setEventWithLabel(statisticsEvent.c_app_Myself_verificationCodeLogin); } sendVerifyCode(delay: Function) {const {state} = this.props; this.dispatch({type: "sendVerifyCode", data: { username: state! .get("loginName") },
                delay
            });
        }

        checkSend(codestr) {
            if (codestr && codestr.length === 4) {
                this.dispatch({ type: "input", data: { password: codestr } });
                setTimeout(() => { this.login(); }); } } renderBody(): JSX.Element | null { const { state } = this.props, codestr = state! .get("codestr");
            return (
                <>
                    <WingBlank>
                        <div className="login-title"> Enter the mobile phone verification code </div> <div className="keyboard-input">
                            <input
                                type="tel"
                                ref={(e) => this.inputRef = e}
                                // className="key-input"
                                onTouchMove={e => {
                                    e.preventDefault();
                                }}
                                maxLength={4}
                                onChange={(event) => {
                                    let value = event.target.value;
                                    // alert(value);
                                    // console.log(value);
                                    this.dispatch({ type: "input", data: { codestr: value } });
                                    this.checkSend(value);
                                    if (value.length + 1 > 4) {
                                        this.tranformClass(value.length)
                                    } else {
                                        this.tranformClass(value.length + 1)
                                    }

                                }}
                                onBlur={() => {
                                    this.tranformClass(5)
                                }}
                                onFocus={() => {
                                    if (codestr) {
                                        if (codestr.length + 1 > 4) {
                                            this.tranformClass(codestr.length)
                                        } else {
                                            this.tranformClass(codestr.length + 1)
                                        }
                                    } else {
                                        this.tranformClass(1)
                                    }
                                }}
                            />
                            <div className="keyboard-span">
                                <span />
                                <span />
                                <span />
                                <span />
                            </div>
                        </div>
                        <div className="input-yzm-box" onClick={() => {
                            this.inputRef.focus()
                        }}><span id="cursorspan1">{codestr && codestr.slice(0, 1)}</span>
                            <span id="cursorspan2">{codestr && codestr.slice(1, 2)}</span>
                            <span id="cursorspan3">{codestr && codestr.slice(2, 3)}</span>
                            <span id="cursorspan4">{codestr && codestr.slice(3, 4)}</span></div>
                        <Countdownauto start={this.sendVerifyCode.bind(this)} content="Obtain captcha code"/> </WingBlank> </> ); }}export const Page = template(Component, state => state[Namespaces.login]);
}


Copy the code

Create an input and four divs, hide the input invisible active focus can be input, four divs map input four input numbers respectively, cursor flicker is also four divs through style animation simulation, the final perfect solution.