I created a TextInput
in React Native,
But I want the label go outside the box with animation when isFocused
or filled with some value like:
If value is null or empty the label must be inside the input, otherwise the label must move outside the input with animation.
My Code:
export default function InputBox(props) {
return (
<View style={styles.container}>
<Text style={styles.label}>{props.label}</Text>
<TextInput
style={styles.input}
autoCapitalize="none"
defaultValue={props.defaultValue}
onChangeText={props.onChangeText}
keyboardType={props.keyboardType}
editable={props.editable}
/>
</View>
);
}
Style:
const styles = StyleSheet.create({
container: {
marginBottom: 20,
backgroundColor: COLORS.WHITE,
paddingTop: 5,
paddingHorizontal: 10,
borderWidth: 1,
borderColor: COLORS.GREY_BORDER,
borderRadius: 2,
},
icon: {
width: 40,
justifyContent: 'center',
alignItems: 'center',
},
input: {
fontFamily: FONT_FAMILY.primaryMedium,
fontSize: 13,
height: 35,
color: COLORS.BLACK,
},
label: {
fontFamily: FONT_FAMILY.primary,
color: COLORS.GREY,
fontSize: 10,
},
});
I created a TextInput
in React Native,
But I want the label go outside the box with animation when isFocused
or filled with some value like:
If value is null or empty the label must be inside the input, otherwise the label must move outside the input with animation.
My Code:
export default function InputBox(props) {
return (
<View style={styles.container}>
<Text style={styles.label}>{props.label}</Text>
<TextInput
style={styles.input}
autoCapitalize="none"
defaultValue={props.defaultValue}
onChangeText={props.onChangeText}
keyboardType={props.keyboardType}
editable={props.editable}
/>
</View>
);
}
Style:
const styles = StyleSheet.create({
container: {
marginBottom: 20,
backgroundColor: COLORS.WHITE,
paddingTop: 5,
paddingHorizontal: 10,
borderWidth: 1,
borderColor: COLORS.GREY_BORDER,
borderRadius: 2,
},
icon: {
width: 40,
justifyContent: 'center',
alignItems: 'center',
},
input: {
fontFamily: FONT_FAMILY.primaryMedium,
fontSize: 13,
height: 35,
color: COLORS.BLACK,
},
label: {
fontFamily: FONT_FAMILY.primary,
color: COLORS.GREY,
fontSize: 10,
},
});
Share
Improve this question
edited Jul 25, 2022 at 16:02
Chance Snow
2,7382 gold badges19 silver badges20 bronze badges
asked Aug 11, 2021 at 6:36
Juhi KukrejaJuhi Kukreja
1871 gold badge5 silver badges14 bronze badges
1
- codesandbox.io/s/gifted-mcnulty-80z7z – gu mingfeng Commented Aug 11, 2021 at 8:40
3 Answers
Reset to default 3If you want to make your own animation you can wrap that label Text into a Animated.View ponent and give animation.
Try this:
import React, { useEffect, useState, useRef } from "react";
import {
View,
StyleSheet,
TextInput,
Text,
Animated,
Pressable,
} from "react-native";
const TestScreen = () => {
const [value, setValue] = useState("");
const moveText = useRef(new Animated.Value(0)).current;
useEffect(() => {
if (value !== "") {
moveTextTop();
} else if (value === "") {
moveTextBottom();
}
}, [value])
const onChangeText = (text: string) => {
setValue(text);
};
const onFocusHandler = () => {
if (value !== "") {
moveTextTop();
}
};
const onBlurHandler = () => {
if (value === "") {
moveTextBottom();
}
};
const moveTextTop = () => {
Animated.timing(moveText, {
toValue: 1,
duration: 200,
useNativeDriver: true,
}).start();
};
const moveTextBottom = () => {
Animated.timing(moveText, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}).start();
};
const yVal = moveText.interpolate({
inputRange: [0, 1],
outputRange: [4, -20],
});
const animStyle = {
transform: [
{
translateY: yVal,
},
],
};
return (
<View style={styles.container}>
<Animated.View style={[styles.animatedStyle, animStyle]}>
<Text style={styles.label}>Enter Your Name</Text>
</Animated.View>
<TextInput
autoCapitalize={"none"}
style={styles.input}
value={value}
onChangeText={(text: string) => onChangeText(text)}
editable={true}
onFocus={onFocusHandler}
onBlur={onBlurHandler}
blurOnSubmit
/>
</View>
);
};
export default TestScreen;
const styles = StyleSheet.create({
container: {
marginBottom: 20,
marginTop: 20,
backgroundColor: "#fff",
paddingTop: 5,
paddingHorizontal: 10,
borderWidth: 1,
borderColor: "#bdbdbd",
borderRadius: 2,
width: "90%",
alignSelf: "center",
},
icon: {
width: 40,
justifyContent: "center",
alignItems: "center",
},
input: {
fontSize: 13,
height: 35,
color: "#000",
},
label: {
color: "grey",
fontSize: 10,
},
animatedStyle: {
top: 5,
left: 15,
position: 'absolute',
borderRadius: 90,
zIndex: 10000,
},
});
The Answer is good but you might not need useEffect in this case, this can be refactored as this
import React, {useState, useRef} from 'react';
import {View, StyleSheet, TextInput, Text, Animated} from 'react-native';
const TestScreen = () => {
const [value, setValue] = useState('');
const moveText = useRef(new Animated.Value(0)).current;
const onFocusHandler = () => {
if (!value) {
moveTextTop();
}
};
const onBlur = () => {
if (!value) {
moveTextBottom();
}
};
const onChangeText = (textValue: string) => {
setValue(textValue);
if (textValue !== '') {
moveTextTop();
} else if (value === '') {
moveTextBottom();
}
};
const moveTextTop = () => {
Animated.timing(moveText, {
toValue: 1,
duration: 200,
useNativeDriver: true,
}).start();
};
const moveTextBottom = () => {
Animated.timing(moveText, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}).start();
};
const yVal = moveText.interpolate({
inputRange: [0, 1],
outputRange: [4, -20],
});
const animStyle = {
transform: [{translateY: yVal}],
};
return (
<View style={styles.container}>
<Animated.View style={[styles.animatedStyle, animStyle]}>
<Text style={styles.label}>Enter Your Name</Text>
</Animated.View>
<TextInput
value={value}
onBlur={onBlur}
style={styles.input}
onFocus={onFocusHandler}
onChangeText={onChangeText}
/>
</View>
);
};
export default TestScreen;
const styles = StyleSheet.create({
container: {
marginBottom: 20,
marginTop: 20,
backgroundColor: '#fff',
paddingTop: 5,
paddingHorizontal: 10,
borderWidth: 1,
borderColor: '#bdbdbd',
borderRadius: 2,
width: '90%',
alignSelf: 'center',
},
icon: {
width: 40,
justifyContent: 'center',
alignItems: 'center',
},
input: {
fontSize: 13,
height: 35,
color: '#000',
},
label: {
color: 'grey',
fontSize: 10,
},
animatedStyle: {
top: 5,
left: 15,
position: 'absolute',
borderRadius: 90,
zIndex: 10000,
},
});
import React, {useRef, useState} from 'react';
import {Animated, ScrollView, StyleSheet, TextInput, View} from 'react-native';
export default function AnimatedTextInput() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [address, setAddress] = useState('');
return (
<ScrollView contentContainerStyle={styles.container}>
<AnimatedInput value={name} onChange={setName} placeholder="Name" />
<AnimatedInput value={email} onChange={setEmail} placeholder="Email" />
<AnimatedInput
value={address}
onChange={setAddress}
placeholder="Address"
multiline
/>
</ScrollView>
);
}
function AnimatedInput({value, onChange, placeholder, multiline}) {
const [inputHeight, setHeight] = useState(null);
const [placeholderWidth, setWidth] = useState(null);
const animation = useRef(new Animated.Value(0)).current;
const translateY = animation.interpolate({
inputRange: [0, 1],
outputRange: [0, -inputHeight / 2],
});
const translateX = animation.interpolate({
inputRange: [0, 1],
outputRange: [0, -placeholderWidth / 4],
});
const scale = animation.interpolate({
inputRange: [0, 1],
outputRange: [1, 0.5],
});
const onFocus = () => animate(1);
const onBlur = () => !value && animate(0);
const animate = val => {
Animated.spring(animation, {
toValue: val,
bounciness: 0,
useNativeDriver: true,
}).start();
};
return (
<View
style={styles.inputContainer}
onLayout={e => !inputHeight && setHeight(e.nativeEvent.layout.height)}>
<View style={{height: inputHeight, ...styles.placeholderContainer}}>
<Animated.Text
style={[
styles.placeholder,
{transform: [{translateY}, {translateX}, {scale}]},
]}
onTextLayout={e =>
!placeholderWidth && setWidth(e.nativeEvent.lines[0]?.width || 0)
}>
{placeholder}
</Animated.Text>
</View>
<TextInput
style={[
styles.input,
multiline && {height: 100, textAlignVertical: 'top'},
]}
onFocus={onFocus}
onBlur={onBlur}
onChangeText={onChange}
multiline={multiline}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
padding: 20,
},
inputContainer: {
borderWidth: 1,
borderRadius: 5,
borderColor: '#999',
marginBottom: 25,
},
input: {
paddingHorizontal: 10,
fontSize: 18,
},
placeholderContainer: {
position: 'absolute',
backgroundColor: 'red',
justifyContent: 'center',
},
placeholder: {
fontSize: 22,
position: 'absolute',
marginHorizontal: 5,
paddingHorizontal: 5,
backgroundColor: '#fff',
color: '#999',
},
});