I have the following code to create a 'dynamic' textinput. A label is placed above the textinput. When the textinput is focused, the label moves above the textinput and the fontsize and color changes. On blur of the textinput, the label moves back (if there is no input in the textinput).
This code contains react-native-reanimated code, should animations should work on the UI thread, not the JS thread.
When I test this code on an IOS simulator (tested iPhone 7 -> iPhone 11 Pro) and I focus on a textinput so the animation runs, the JS thread drops about 3-6 frames (54-57 fps), which is OK I guess. On a real device (iPhone 7), the JS thread drops about 20-30 frames (and sometimes even more). When I start typing in the textinput, I get a slow callback (I check the input of the textinput while typing). On the simulator, the callback is immediately. On the real device, it sometimes takes up to 2 seconds before the input is checked.
import React, { useState, useEffect } from 'react';
import { View, TextInput } from 'react-native';
import Animated, { Easing, Extrapolate } from 'react-native-reanimated';
import { interpolateColor, useTimingTransition } from 'react-native-redash';
import Colors from '../../constants/Colors';
const { interpolate } = Animated;
const AuthenticationInput = React.forwardRef((props, ref) => {
const {
ref,
label,
onChangeText,
secureTextEntry,
returnKeyType,
icon
} = props;
const [value, setValue] = useState('');
const [trans, setTrans] = useState(0);
const transition = useTimingTransition(trans, {
duration: 250,
easing: Easing.inOut(Easing.ease)
});
// move the label in the x direction
const moveX = interpolate(transition, {
inputRange: [0, 1],
outputRange: [12.5, 0],
extrapolate: Extrapolate.CLAMP
});
// move the label in the y direction
const moveY = interpolate(transition, {
inputRange: [0, 1],
outputRange: [12.5, -20],
extrapolate: Extrapolate.CLAMP
});
// change the font size of the label
const fontSize = interpolate(transition, {
inputRange: [0, 1],
outputRange: [15, 12.5],
extrapolate: Extrapolate.CLAMP
});
// change the color of the label
const color = interpolateColor(transition, {
inputRange: [0, 1],
outputRange: ['#aaa', '#000']
});
// pass the input of the textinput to the
// onChangeText function passed as a prop
useEffect(() => {
onChangeText(value)
}, [value]);
return (
<View style={{
marginHorizontal: 25,
marginTop: 25,
borderRadius: 5,
backgroundColor: Colors.white
}}
>
<Animated.View style={{
position: 'absolute',
transform: [{ translateX: moveX }, { translateY: moveY }]
}}
>
<Animated.Text style={{
fontSize,
color
}}
>
{label}
</Animated.Text>
</Animated.View>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<TextInput
ref={ref}
autoCapitalize="none"
autoCompleteType="off"
autoCorrect={false}
secureTextEntry={secureTextEntry}
onChangeText={(v) => setValue(v)}
style={{
margin: 0,
padding: 12.5,
flex: 1,
fontSize: 15
}}
hitSlop={{
top: 10,
right: 10,
bottom: 10,
left: 10
}}
onFocus={() => setTrans(1)}
onBlur={() => {
if (value.length === 0) {
setTrans(0);
}
}}
returnKeyType={returnKeyType}
/>
<View style={{ padding: 12.5 }}>
{icon}
</View>
</View>
</View>
);
});
export default AuthenticationInput;