div高仿出来input,拉起高仿的数字键盘?

大家都知道,H5的input框,唤醒手机的键盘在android和ios上展现的不同。需求要求实现我们这边实现一个自己的键盘,无奈需求方还是大佬,搬砖者还是乖乖的想想实现(笔者这边采用的react框架,所以采用了antd mobile组件库)

首先要实现一个键盘,大家的思路是怎么样的呢?

  • 用li循环呗,9键出来。
  • 用div模拟input + 光标。

第一个很是简单,用组件库的Modal里面嵌套我们自己的循环呗,就这样整呗。

tsx

const numLen = [1, 2, 3, 4, 5, 6, 7, 8, 9]

export default class BcMobileKeyboard extends React.Component<any, any>{
    constructor(props) {
        super(props)
    }
    // 获取当前点击的数字,调用父亲的方法传递过去
    getCurNum = (num) => {
        this.props.getNumber(num)
    }
    // 删除当前的数字
    delNum = () => {
        this.props.delNumber()
    }
    // 点击完成的触发
    complate = () => {
        this.props.complate()
    }
    render () {
        const { className, visible = true, maskClosable = false, animationType = 'slide-up', popup = true } = this.props
        return (
            <Modal
                className={`key-board-box ${className}`}
                popup={popup}
                maskClosable={maskClosable}
                visible={visible}
                animationType={animationType}
            >
                <div className='board-top'>
                    <button onClick={this.complate.bind(this)}>完成</button>
                </div>
                <ul className="board-num">
                    {
                        numLen.map(item => {
                            return <li onClick={this.getCurNum.bind(this, item)}>{item}</li>
                        })
                    }
                    {/* 这个li单纯为了布局,让它占住位置 */}
                    <li style={{visibility: 'hidden'}}></li>
                    <li className="zero" onClick={this.getCurNum.bind(this, 0)}>0</li>
                    {/* 删除的icon, 如果复制需要替换这个成你们自己的icon */}
                    <li><i className="icon iconfont del-icon" onClick={this.delNum.bind(this)}>&#xe602;</i></li>
                </ul>
            </Modal>
        )
    }
}
复制代码

键盘的样式

@import 'assets/style/px2rem.scss';
.key-board-box{
    overflow: hidden;
    -moz-user-select:none;/*火狐*/
    -webkit-user-select:none;/*webkit浏览器*/
    -ms-user-select:none;/*IE10*/
    -khtml-user-select:none;/*早期浏览器*/
    user-select:none;
    -webkit-touch-callout:none ;
    -webkit-text-size-adjust:none ;
    -webkit-tap-highlight-color:transparent ;
    -webkit-user-select:none ;
    .board-top{
        height: px2rem(44);
        display: flex;
        justify-content: flex-end;
        align-items: center;
        button {
            outline: none;
            border: 0;
            padding-right: px2rem(15);
            color: #508CEE;
            font-size: px2rem(18);
        }
    }
    .board-num{
        background: #D2D5DB;
        overflow: hidden;
        height: px2rem(216);
        padding-left: px2rem(6);
        li{
            margin-right: px2rem(6);
            margin-top: px2rem(6);
            width: 31.7%;
            float: left;
            height: px2rem(46);
            background:rgba(255,255,255,1);
            box-shadow:0px 1px 0px 0px rgba(132,134,136,1);
            border-radius: px2rem(5);
            display: flex;
            justify-content: center;
            align-items: center;
            font-size: px2rem(25);
            font-family:Helvetica;
            color:rgba(0,0,0,1);
            
        }
        .zero{
            margin-bottom: px2rem(5);
        }
        li:last-child {
            background: none;
            border: none;
            box-shadow: none;
            .del-icon {
                color: #3F434A;
            }
        }
    }
}
复制代码

其实主要的就是有了键盘, 那么我们的input框是不是要有啊?那就高仿一个呗?开整

思考问题:

  1. placeholder的存在?
  2. 一闪一闪的光标存在?光标具体存在的位置?
  3. 他的点击事件?(没有拓展失焦、聚焦功能)?

说一下我的具体思路把。

  • 初始我在input框里没有任何内容的时候,我用伪类来实现的光标的存在。(css动画)
  • 就是input框的内容我任意点击光标的位置改变,采用的是每一个数字我都是一个span,点击会拿取下标,在添加空的span再来实现光标。
  • 我用'only'做的唯一标示,让我更有利的判断。
  • 关闭弹窗的时候,我这个input的span都要删掉,也就是将数组清空。

tsx

import React from 'react'
import './style.scss'
import { BcMobileKeyboard } from 'container/index'

interface Rules {
    className?: string,
    placeholder?: string,
    isShowBoard?: boolean,
    max?: number,
    refs?: Function,
    notifyComplate?: Function,
    onRef?: any
}

export default class BcImitateInput extends React.Component<Rules, any>{
    constructor(props) {
        super(props)
        this.state = {
            curNum: [], // 用来记录当前div里面的所有值
            selectedInd: null // 当前点击的光标的位置
        }
    }
    // 拿到键盘点击的数字
    setNumberHandle = (num) => {
        let { curNum, selectedInd } = this.state
        let flag = curNum.some((item) => {return item == 'only'}) // 数据存在only,就表示光标在中间存在过
        let max = flag ? this.props.max + 1 : this.props.max // 光标只有一次,所有她的最大值也加1
        if (curNum.length >= max) return false
        if (selectedInd != null) { // 表示光标在中间存在过 增删内容
            curNum.splice(selectedInd, 0, num) // 添加内容
            this.setState({
                curNum,
                selectedInd: selectedInd + 1 // 光标位置也随之增加
            })
        } else { // 表示正常流程走下来。
            curNum.push(num)
            this.setState({
                curNum
            })
        }
    }
    // 删除键盘点击的数字
    delNumberHandle = () => {
        let { curNum, selectedInd } = this.state
        if (curNum.length) {
            if (selectedInd - 1 >= 0) { // 表示从中间删除内容
                curNum.splice(selectedInd - 1, 1)
                this.setState({
                    curNum,
                    selectedInd: selectedInd - 1 // 当然光标随之减少
                })
            } else if (selectedInd == null) { // 表示正常的删除内容
                curNum.pop()
                this.setState({
                    curNum
                })
            }
        }
    }
    // 点击键盘中的完成
    complateHandle = () => {
        let curNum = this.state.curNum
        for (let i = 0; i < curNum.length; i++) {
            if (curNum[i] == 'only') {
                curNum.splice(i, 1)
                break;
            }
        }
        this.props.notifyComplate(curNum.join('')) // 准确的把当前的数据通知出去
    }
    // 高仿的input的点击事件
    inputClickHandle = (e) => {
        // 这里是为了点击div的任何地方  如果不在数字的中间,那么我们要让光标回到最后的位置
        let { curNum, selectedInd } = this.state
        if (curNum.length && selectedInd != null) {
            for (let i = 0; i < curNum.length; i++) {
                if (curNum[i] == 'only') {
                    curNum.splice(i, 1)
                    break;
                }
            }
            this.setState({
                selectedInd: null
            })
        }
    }
    // 点击当前的span获取当前光标的位置
    curNumHandle = (ind, e) => {
        if (e) { // 为了阻止冒泡
            e.stopPropagation();
            e.preventDefault();
        } else {
            window.event.returnValue = false;
            window.event.cancelBubble = true;
        }
        let { curNum } = this.state
        for (let i = 0; i < curNum.length; i++) {
            if (curNum[i] == 'only') {
                curNum.splice(i, 1)
                break;
            }
        }
        curNum.splice(ind, 0, 'only')
        this.setState({
            curNum,
            selectedInd: ind
        })
    }
    // 初始化数据  就是为了关闭这个弹窗的时候,数据不可以保留
    initCodeNum = () => {
        this.setState({
            curNum: [],
            selectedInd: null
        })
    }
    setClass = () => {
        // 这里的class是为了用伪类来实现一些光标
        let { curNum, selectedInd } = this.state
        if (selectedInd == null && !curNum.length) return 'not'
        if (selectedInd != null) return ''
        if (selectedInd == null && curNum.length) return 'yes'
    }
    render() {
        const { curNum } = this.state
        const { placeholder = '输入验证码', className, isShowBoard } = this.props
        return (
            <div>
                <div className={`${this.setClass()} ${className} imitate-input`} placeholder={placeholder} onClick={this.inputClickHandle}>
                    {
                        curNum.length ? curNum.map((ele, index) => {
                            return <span className={`${ele == 'only' ? 'yes' : ''}`}
                                onClick={(e) => { this.curNumHandle(index, e) }}>
                                {ele == 'only' ? '' : ele}
                            </span>
                        }) : ''
                    }
                </div>
                {/* 就是上文的组件 */}
                <BcMobileKeyboard
                    visible={isShowBoard}
                    getNumber={this.setNumberHandle}
                    complate={this.complateHandle}
                    delNumber={this.delNumberHandle}>
                </BcMobileKeyboard>
            </div>
        )
    }
    componentWillMount ():void { // 把this 传递父级,可以做更多的事情
        this.props.onRef(this)
    }
}

复制代码

input的样式

@import 'assets/style/px2rem.scss';
.imitate-input{
    display: flex;
    align-items: center;
    font-size:px2rem(16);
    border: px2rem(1) solid #999999;
    user-select:text;
    -webkit-user-select:text;
    .selected::after{
        content:'';
        display: block;
        width:1px;
        height:16px;
        animation: 1s steps(1, start) 0s normal none infinite running blink;
    }
}
.yes::after{
    content:'';
    display: block;
    width:1px;
    height:16px;
    animation: 1s steps(1, start) 0s normal none infinite running blink;
}
.not:empty::before{
    content:'';
    display: block;
    width:1px;
    height:16px;
    animation: 1s steps(1, start) 0s normal none infinite running blink;
}
.not:empty::after{
    content: attr(placeholder);
    color: #999999;
    font-size:px2rem(16);
}
@keyframes blink {
 0%{
   background-color: white;
 }
 50% {
   background-color: #000000;
 }
 100% {
   background-color: white;
 }
}

复制代码

具体的代码就是以上的实现,如有错误,请多指正。有更好的想法实现,欢迎评论。大家共勉~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值