117 lines
4.0 KiB
TypeScript
117 lines
4.0 KiB
TypeScript
import React, { useRef, useState } from 'react';
|
|
import { Animated, Keyboard, Text, TextInput, TouchableOpacity, View } from 'react-native';
|
|
|
|
const VerificationCodeInput = () => {
|
|
const [code, setCode] = useState(['', '', '', '', '', '']);
|
|
const refs = useRef([]);
|
|
const shakeAnim = useRef(new Animated.Value(0)).current;
|
|
|
|
const focusNext = (index, value) => {
|
|
if (value && index < 5) {
|
|
refs.current[index + 1].focus();
|
|
}
|
|
};
|
|
|
|
const focusPrevious = (index, key) => {
|
|
if (key === 'Backspace' && index > 0 && !code[index]) {
|
|
refs.current[index - 1].focus();
|
|
}
|
|
};
|
|
|
|
const handleCodeChange = (text, index) => {
|
|
const newCode = [...code];
|
|
newCode[index] = text;
|
|
setCode(newCode);
|
|
focusNext(index, text);
|
|
};
|
|
|
|
const handleSubmit = () => {
|
|
const fullCode = code.join('');
|
|
if (fullCode.length === 5) {
|
|
Keyboard.dismiss();
|
|
// 这里处理验证逻辑
|
|
console.log('验证码:', fullCode);
|
|
} else {
|
|
// 抖动动画效果
|
|
Animated.sequence([
|
|
Animated.timing(shakeAnim, {
|
|
toValue: 10,
|
|
duration: 50,
|
|
useNativeDriver: true
|
|
}),
|
|
Animated.timing(shakeAnim, {
|
|
toValue: -10,
|
|
duration: 50,
|
|
useNativeDriver: true
|
|
}),
|
|
Animated.timing(shakeAnim, {
|
|
toValue: 10,
|
|
duration: 50,
|
|
useNativeDriver: true
|
|
}),
|
|
Animated.timing(shakeAnim, {
|
|
toValue: 0,
|
|
duration: 50,
|
|
useNativeDriver: true
|
|
})
|
|
]).start();
|
|
}
|
|
};
|
|
|
|
const handleClear = () => {
|
|
setCode(['', '', '', '', '']);
|
|
refs.current[0].focus();
|
|
};
|
|
|
|
return (
|
|
<View className="w-full mt-8">
|
|
<Animated.View
|
|
style={{
|
|
transform: [{ translateX: shakeAnim }]
|
|
}}
|
|
className="flex flex-row justify-center space-x-3 mb-6 gap-[1rem]"
|
|
>
|
|
{code.map((digit, index) => (
|
|
<TextInput
|
|
key={index}
|
|
ref={ref => refs.current[index] = ref}
|
|
className="w-16 h-16 bg-[#FFF8DE] rounded-xl text-textTertiary text-3xl text-center"
|
|
keyboardType="number-pad"
|
|
maxLength={1}
|
|
value={digit}
|
|
onChangeText={text => handleCodeChange(text, index)}
|
|
onKeyPress={({ nativeEvent: { key } }) => focusPrevious(index, key)}
|
|
selectTextOnFocus
|
|
caretHidden={true}
|
|
/>
|
|
))}
|
|
</Animated.View>
|
|
|
|
<View className="flex-row justify-center space-x-4">
|
|
<TouchableOpacity
|
|
onPress={handleClear}
|
|
className="px-6 py-3 border border-purple-400 rounded-full"
|
|
>
|
|
<Text className="text-purple-300 font-medium">清除</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
onPress={handleSubmit}
|
|
className="bg-amber-400 px-8 py-3 rounded-full shadow"
|
|
activeOpacity={0.8}
|
|
>
|
|
<Text className="text-purple-900 font-bold">验证</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
|
|
<View className="mt-6 flex-row justify-center space-x-2">
|
|
<Text className="text-purple-300">未收到验证码?</Text>
|
|
<TouchableOpacity>
|
|
<Text className="text-amber-400 font-medium">重新发送</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
);
|
|
};
|
|
|
|
export default VerificationCodeInput; |