近期,大把時間花費在浪費生命的手機鍵盤中,最後無奈被各大機型的各類兼容性戰勝~css
你覺得我這樣就輸了嗎? No No No !html
來自產品方的需求:(驗證碼頁面)react
1. 拉起數字鍵盤瀏覽器
2. 只容許用戶輸入6位數字bash
哎吆喂,就2個需求嘛,so easy!開始咱們的input的type爲number,給它來個最大的可輸入長度。盤它!你會發現無論是在安卓手機仍是蘋果手機,系統自帶的輸入法,用戶裝的輸入法,各式各樣的奇葩都在裏面。再加上在各類瀏覽器裏面也會有所不一樣,再加上樣式展示的不一樣,與其折磨本身,不如去參考別人的實現。巧了,思路由此展開。antd
根據上面異樣,給出了實現的答案。dom
1. 本身用div去模仿假的input,用li循環鍵盤。svg
2. 在安卓端用本身寫的高仿的,在蘋果端仍是用自帶的鍵盤。學習
以上解析你可能會有疑問?優化
本身模擬的input,能夠在數字的中間刪除數字,光標的位置能夠改變嗎?
爲何要在安卓端和蘋果端寫入的鍵盤要不一致?
第一個問題,咱們針對這個沒有作處理,若有須要,請查看筆者的文章,用div模仿的input,裏面有思路如何改變光標。
第二個問題是由於蘋果端有提供咱們更加便捷的方式,利大於弊。點擊驗證碼直接進入輸入框,減小用戶的操做。
開始步入正題,參考antd mobile的自定義鍵盤,開發咱們本身的鍵盤。
本身高仿鍵盤的組件
import React from 'react';
import TouchFeedback from 'rmc-feedback'; // 點擊數字高亮的插件
import IconSvg from './iconSvg' // 這個是x號的svg,你們能夠替換本身的
import './style.scss'
const numArray = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '', '0']
interface KeyboardItemProps {
children?: any,
className?: string,
onClick?: () => void
}
class KeyboardItem extends React.Component<KeyboardItemProps, any> {
render() {
const { children, onClick, className = '' } = this.props
let value = (className == 'delete' ? className : children)
return (
<TouchFeedback activeClassName={`active`}>
<li
onClick={e => onClick(e, value as string)}
className={`${children == '' ? 'hidden' : className}`}
>
{children}
</li>
</TouchFeedback>
)
}
}
class CustomKeyboard extends React.Component<any, any> {
// 每個數字的點擊事件
onKeyboardClick = (e?, value?: string) => {
e.nativeEvent.stopImmediatePropagation();
this.props.onKeyboardClick(value)
}
// 每個鍵盤的render
renderKeyboardItem = (item: string, index: number) => {
return (
<KeyboardItem key={`item-${item}-${index}`} onClick={this.onKeyboardClick}>{item}</KeyboardItem>
)
}
render() {
const { className } = this.props
return (
<div className={`keyboard-box ${className}`}>
<div className="custom-header">
<div className="complete" onClick={(e) => this.onKeyboardClick(e, 'complete')}>完成</div>
</div>
<ul className={`number-keyboard`}>
{
numArray.map((item, index) => this.renderKeyboardItem(item, index))
}
<KeyboardItem className="delete" key={`item-${10}`} onClick={this.onKeyboardClick}>
<i><IconSvg color="#3F434A;"/></i>
</KeyboardItem>
</ul>
</div>
)
}
}
export default CustomKeyboard
複製代碼
本身高仿的input,用僞類作光標。
import React from 'react'
import CustomKeyboard from '../CustomKeyboard'
import './style.scss'
interface NumberInputProps {
placeholder?: string,
editInput?: boolean,
disabled?: boolean,
value?: any,
className?: string,
maxLength?: number,
fakeInputClassName?: string,
children?: any,
labelNumber?: number,
extra?: any,
onChange?: (value: string) => void,
onBlur?: (value: string) => void,
onExtraClick?: () => void,
}
class YzmInput extends React.Component<NumberInputProps, any>{
constructor(props) {
super(props)
this.state = {
focus: false, // 默認不聚焦
value: props.value || '',
}
}
fakeInput = null
// 僞造的input的點擊事件
onFakeInputClick = () => {
this.focus()
}
focus = () => {
this.removeBlurListener()
const { focus } = this.state
if (!focus) {
this.onInputFocus()
}
setTimeout(() => {
this.addBlurListener()
}, 50)
}
onChange = (value: any) => {
this.setState({ value })
this.props.onChange(value)
}
onInputFocus = () => {
this.setState({
focus: true
})
}
onInputBlur = (value: string) => {
const { focus } = this.state
if (focus) {
this.setState({
focus: false
})
this.props.onBlur && this.props.onBlur(value)
}
}
doBlur = (ev: MouseEvent) => {
const { value } = this.state;
if (ev.target !== this.fakeInput) {
this.onInputBlur(value);
}
}
// addBlurListener,removeBlurListener就是爲了處理blur&focus事件
addBlurListener = () => {
document.addEventListener('click', this.doBlur, false);
}
removeBlurListener = () => {
document.removeEventListener('click', this.doBlur, false);
}
// 是點擊鍵盤的每個dom的處理方式
handleKeyboard = (keyboardVal: string) => {
const { maxLength } = this.props
const { value } = this.state
const { onChange } = this
let valueAfterChange
if (keyboardVal === 'delete') {
valueAfterChange = value.substring(0, value.length - 1)
onChange(valueAfterChange)
} else if (keyboardVal === 'complete') {
valueAfterChange = value
onChange(valueAfterChange)
this.setState({
focus: false
})
} else {
if (maxLength !== undefined && maxLength >= 0 && (value + keyboardVal).length > maxLength) {
valueAfterChange = (value + keyboardVal).substr(0, maxLength)
onChange(valueAfterChange)
} else {
valueAfterChange = value + keyboardVal
onChange(valueAfterChange)
}
}
}
render() {
let { value, focus } = this.state
const {
placeholder = '輸入驗證碼',
editInput = true,
disabled = false,
className = '',
fakeInputClassName = '',
labelNumber = 1,
children,
extra,
onExtraClick
} = this.props
const preventKeyboard = disabled || !editInput
return (
<>
<div className={`${className} fake-input-box`}>
{children ? (<div style={{ width: 16 * labelNumber + 'px' }} className="label">{children}</div>) : ''}
<div className='fake-input-content' onClick={preventKeyboard ? () => { } : this.onFakeInputClick}>
{value == '' && (<div className="fake-input-placeholder">{placeholder}</div>)}
<div
className={`${fakeInputClassName} fake-input ${focus ? 'focus' : ''}`}
role="textbox"
aria-label={value || placeholder}
ref={el => this.fakeInput = el}
>
{value}
</div>
</div>
{extra && <div className="extra" dangerouslySetInnerHTML={{ __html: extra }} onClick={onExtraClick}></div>}
</div>
<CustomKeyboard className={focus ? '' : 'none'} onKeyboardClick={this.handleKeyboard} />
</>
)
}
}
export default YzmInput
複製代碼
實際在頁面中的使用。
<div className="yzm-box">
<BcNumberInput
className="yzm-input"
labelNumber={4}
maxLength={6}
onChange={(value) => change(value, flagTime)}
placeholder="輸入驗證碼"
extra={`|<div id=${flagTime ? 'timer' : 'retrieve'}>${flagTime ? time + '秒後重發' : '從新獲取'}</div>`}
onExtraClick={flagTime ? () => {} : () => click(this.actionCountDown.bind(this))}
>驗證碼</BcNumberInput>
</div>
複製代碼
以上代碼不足的地方還有不少。基於如今的實現已經知足我方這邊的需求,後續還會在優化~
問題。(antd mobile的自定義鍵盤已經實現的很完美,能夠參考下)
假設個人input在底部的位置,那麼我鍵盤擡起的時候,是否是須要將滾動條往上移,讓用戶輸入的同時可看見輸入裏面的內容。
還有咱們的鍵盤是否是能夠作成用戶可配置,可配置鍵盤的內容,可配置鍵盤的頭部。
等等一系列,還須要咱們學習的地方,小夥伴們,Come on ! 有不足的地方,歡迎指正。