2022 年 6 月 25 日
更新于 2022 年 7 月 6 日
简介
每次用到验证码输入的时候都会幻想自己实现一个, 今天在高铁上百无聊赖, 于是动手做了一个.
演示
话不多说, 先上演示: demo .
代码
VerificationCode.js
import { useState } from 'react';
import './VerificationCode.scss';
import $ from 'jquery';
/**
* Replace the nth char of a string with another specified char.
* @param {String} str the str that needs to be modified
* @param {int} index the index for change
* @param {character} char the new char
* @returns
*/
function replaceChar(str, index, char) {
let strArray = str.split("");
strArray[index] = char;
return strArray.join("");
}
export function VerificationCode() {
// The code
const [code, setCode] = useState(" ");
// Current focused position
const [focusPosition, setFocusPosition] = useState(-1);
// Processing
const [processing, setProcessing] = useState(false);
/**
* Handles body click.
* @returns void
*/
function handleClick() {
if (processing) {
return;
}
// focus the input
$("#code").focus();
// last position, do nothing
if (focusPosition == 5) {
return;
}
// initialization
if (focusPosition == -1) {
setFocusPosition(0);
} else { // activate
setFocusPosition(focusPosition);
}
}
/**
* Handles input value change.
* @param {*} e change event
* @returns nothing
*/
async function handleChange(e) {
// get input value
let value = e.target.value;
// get input value length
let length = value.length;
if (length == 6) {
setProcessing(true);
sleep(1000).then(() => {
setProcessing(false);
reset();
});
}
// if length is greater than 6, revert back
if (length > 6) {
$("#code").val(code.substring(0, 6));
return;
}
// if current focus position is less than the previous position
// then set the last char to space.
if (length - 1 < focusPosition) {
let modifiedCode = replaceChar(code, focusPosition, ' ');
setCode(modifiedCode);
setFocusPosition(focusPosition - 1)
return;
}
// set current focus position
let index = length - 1 >= 0 ? length - 1 : 0;
// update current focused index
setFocusPosition(index);
// update and set code
setCode(replaceChar(code, index, value.charAt(value.length - 1)));
}
function reset() {
setCode(' ');
setFocusPosition(-1);
$('#code').val('');
}
return (
<div className={`playground`}>
{/* <div className={'my-logo'}>
<div className={'d'}>
d
</div>
</div> */}
<div className={'label'}>
请输入验证码:
</div>
<div
className={"numbers"}
onClick={handleClick}>
{code.split("").map((item, key) => (
<div key={key} className={"number " + ((focusPosition == key && !processing) ? "focused" : "")}>
{item}
</div>
))}
<input id={"code"} type={'number'} maxLength={6} onChange={handleChange}/>
</div>
{(!processing) && (
<div className={'button'} onClick={reset}>重新输入</div>
)}
{(processing) && (
<div className={'processing'} onClick={reset}>验证中...</div>
)}
</div>
);
}
VerificationCode.scss
.verification-code {
width: 100%;
/* Chrome, Safari, Edge, Opera */
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Firefox */
input[type=number] {
-moz-appearance: textfield;
}
.label {
padding: 10px;
}
#code {
opacity: 0;
cursor: default;
width: 0;
height: 0;
}
.numbers {
padding: 10px;
display: flex;
.number {
width: 40px;
height: 40px;
border: 1px solid #bbb;
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
font-weight: bold;
font-size: 20px;
color: #888;
cursor: text;
&:nth-last-child(1) {
margin-right: 0;
}
}
.focused {
border: 1px solid #888;
box-shadow: 0 5px 20px rgba(0,0,0, 0.2);
}
}
.button {
margin: 10px;
cursor: pointer;
font-weight: bold;
color: #0087fc;
width: fit-content;
&:hover {
text-decoration: underline;
}
}
.processing {
margin: 10px;
color: #888;
font-weight: bold;
}
}