React Native를 사용하여 애니메이션 텍스트 필드 만들기
Animated
도서관은 그때 이후 큰 변화가 없었다.그래서 나는 같은 원칙을 사용하여 처음부터 새로운 React NativeTextInput
구성 요소를 구축할 것이다.이번에 나는
material-ui
애니메이션을 실현할 것이다.이것은 간단한 예이기 때문에 나는 이것이React Native 애니메이션의 기초 지식을 이해하는 유용한 예라고 생각한다.Github에서 이 구성 요소의 전체 버전을 찾을 수 있습니다.react-native-web 덕분에 나는 리액션 코드를 웹에 이식하는 데도 성공했다.너는 내 핸드폰에서 놀 수 있다blog.
미리 보기에서 보듯이 TextField에는 다음과 같은 네 가지 주요 UI 상태가 있습니다.
기본 텍스트 필드부터 시작
기본
TextField
부터 시작하여 react native Text Input을 확장하고 스타일을 설정합니다.import React from 'react';
import { StyleSheet, TextInput } from 'react-native';
// extend from native TextInput props
type Props = React.ComponentProps<typeof TextInput>;
const TextField: React.FC<Props> = (props) => {
/*
** spread operator helps to extract style prop and assign
** any remaining props to the `restOfProps` variable.
** it is pretty handy here as we need to support
** all the props the native TextInput component has.
*/
const { style, ...restOfProps } = props;
return <TextInput style={[style, styles.input]} {...restOfProps} />;
};
const styles = StyleSheet.create({
input: {
padding: 24,
borderColor: '#B9C4CA',
borderWidth: 1,
borderRadius: 4,
fontFamily: 'Avenir-Medium',
fontSize: 16
}
});
export default TextField;
출력은 경계선과 자리 표시자 텍스트가 있는 간단한 텍스트 입력입니다.<TextField placeholder="Cardholder name" />
태그 만들기
placeholder
입력이 비어 있을 때만 프롬프트가 탭 역할을 합니다.이것은 우리의 사례에 있어서 아직 부족하기 때문에 사용자 정의 탭을 만들어서 입력의 맨 위에 표시해야 합니다.부모
View
로 TextInput을 포장하고 다른 View
를 탭으로 만드는 형제항을 만듭니다. 탭을 맞춤형으로 만들 수 있는 공간이 넓어집니다.우리는 라벨 position: absolute
양식을 사용하여 TextInput
의 맨 위에 있는지 확인할 것입니다.TextInput
라는 새 아이템으로 원생label
모듈 아이템을 확장하였습니다. 이것은 TextField
의 유일한 아이템이 될 것입니다.-type Props = React.ComponentProps<typeof TextInput>
+type Props = React.ComponentProps<typeof TextInput> & {
+ label: string
+}
const TextField: React.FC<Props> = (props) => {
- const { style, ...restOfProps } = props
+ const { label, style, ...restOfProps } = props
return (
- <TextInput
- style={[style, styles.input]}
- {...restOfProps}
- />
+ <View style={style}>
+ <TextInput style={styles.input} {...restOfProps} />
+ <View style={styles.labelContainer}>
+ <Text style={styles.label}>{label}</Text>
+ </View>
+ </View>
)
}
const styles = StyleSheet.create({
+ labelContainer: {
+ position: 'absolute',
+ left: 16,
+ top: -6,
+ paddingHorizontal: 8,
+ backgroundColor: 'white',
+ },
+ label: {
+ fontFamily: 'Avenir-Heavy',
+ fontSize: 12,
+ },
TextField
지금 보니까 이렇게 생겼어요.초점 상태 기반 태그 찾기
탭은 초점 상태에 따라 입력한 중심과 위쪽 사이를 이동해야 합니다.우선 애니메이션 없이 내부
isFocused
상태에 따라 탭을 간단하게 배치합시다.우리는
TextInput
sonBlur
와 onFocus
방법을 감청하고 그것들에 따라 isFocused
상태를 수정할 수 있다.top
상태에서 라벨을 조종하는 isFocused
양식에 따라 라벨을 재배치하기에 충분하다.또한 레이블의 글꼴 크기와 색상도 수정할 것입니다.const TextField: React.FC<Props> = (props) => {
const {
label,
style,
+ onBlur,
+ onFocus,
...restOfProps
} = props
+ const [isFocused, setIsFocused] = useState(false)
return (
<View style={style}>
- <TextInput style={styles.input} {...restOfProps} />
- <View style={styles.labelContainer}>
- <Text style={styles.label}>{label}</Text>
+ <TextInput
+ style={styles.input}
+ {...restOfProps}
+ onBlur={(event) => {
+ setIsFocused(false)
+ onBlur?.(event)
+ }}
+ onFocus={(event) => {
+ setIsFocused(true)
+ onFocus?.(event)
+ }}
+ />
+ <View
+ style={[
+ styles.labelContainer,
+ {
+ top: isFocused ? -6 : 24,
+ },
+ ]}
+ >
+ <Text
+ style={[
+ styles.label,
+ {
+ fontSize: isFocused ? 12 : 16,
+ color: isFocused ? '#080F9C' : '#B9C4CA',
+ },
+ ]}
+ >
+ {label}
+ </Text>
</View>
</View>
태그에 애니메이션 설정하기
우리는 지금 초점 상태에 기반한 라벨을 가지고 있다.React Native는 애니메이션을 만들 수 있는 내장
Animated
구성 요소를 가지고 있습니다. 이것은 우리의 간단한 애니메이션을 지원하기에 충분합니다.포커스 상태를 표시하기 위해 Animated.Value
를 만들고, 포커스 스타일을 표시하기 위해 삽입할 것입니다.Animated.Value
숫자 매개 변수를 받아들이기 때문에 우리는 숫자로 isFocused
상태를 표시해야 한다.나는 0을 사용하여 초점이 맞지 않는 상태를 표시하고, 1은 초점이 맞지 않는 상태를 표시할 것이다. const [isFocused, setIsFocused] = useState(false)
+ const focusAnim = useRef(new Animated.Value(0)).current
+
+ /*
+ ** This effect will trigger the animation every
+ ** time `isFocused` value changes.
+ */
+ useEffect(() => {
+ Animated.timing(focusAnim, {
+ toValue: isFocused ? 1 : 0,
+ // I took duration and easing values
+ // from material.io demo page
+ duration: 150,
+ easing: Easing.bezier(0.4, 0, 0.2, 1),
+ // we'll come back to this later
+ useNativeDriver: false,
+ }).start()
+ }, [focusAnim, isFocused])
+
return (
<View style={style}>
- <View
+ <Animated.View
style={[
styles.labelContainer,
{
- top: isFocused ? -6 : 24,
+ top: focusAnim.interpolate({
+ inputRange: [0, 1],
+ outputRange: [24, -6],
+ }),
},
]}
>
- <Text
+ <Animated.Text
style={[
styles.label,
{
- fontSize: isFocused ? 12 : 16,
+ fontSize: focusAnim.interpolate({
+ inputRange: [0, 1],
+ outputRange: [16, 12],
+ }),
color: isFocused ? '#080F9C' : '#B9C4CA',
},
]}
>
{label}
- </Text>
- </View>
+ </Animated.Text>
+ </Animated.View>
</View>
)
}
네이티브 드라이버 사용
우리의 애니메이션은 현재 효과가 매우 좋다.그러나 우리는
useNativeDriver
파라미터를 Animated
API에 전달함으로써 저급 장치에서 더욱 매끄럽게 할 수 있다.다음은 React Nativedocumentation에 대한 설명입니다.
By using the native driver, we send everything about the animation to native before starting the animation, allowing native code to perform the animation on the UI thread without having to go through the bridge on every frame. Once the animation has started, the JS thread can be blocked without affecting the animation.
문제는 이 드라이버는 유한한 속성 집합, 예를 들어
transform
과 opacity
를 사용할 수 있다는 것이다.그래서 top
와 fontSize
속성에 적용되지 않기 때문에 지원하는 속성으로 대체해야 합니다.Animated
설정useNativeDriver: true
시 예외 발생:다행히도
transform
여기서 같은 애니메이션 행동을 만들 수 있습니다.우리는 scale
속성 대체fontSize
를 사용하고 translateY
를 사용하여 탭을 이동할 것이다.불행하게도, scale
변환을 사용하면 라벨이 x축에서 이동할 수 있습니다.내가 찾을 수 있는 유일한 해결 방법은 추가 translateX
변환을 만들고 수동으로 x축 이동을 취소하는 것이다. style={[
styles.labelContainer,
{
- top: focusAnim.interpolate({
- inputRange: [0, 1],
- outputRange: [24, -6],
- }),
+ transform: [
+ {
+ scale: focusAnim.interpolate({
+ inputRange: [0, 1],
+ outputRange: [1, 0.75],
+ }),
+ },
+ {
+ translateY: focusAnim.interpolate({
+ inputRange: [0, 1],
+ outputRange: [24, -12],
+ }),
+ },
+ {
+ translateX: focusAnim.interpolate({
+ inputRange: [0, 1],
+ outputRange: [16, 0],
+ }),
+ },
+ ],
},
]}
>
- <Animated.Text
+ <Text
style={[
styles.label,
{
- fontSize: focusAnim.interpolate({
- inputRange: [0, 1],
- outputRange: [16, 12],
- }),
color: isFocused ? '#080F9C' : '#B9C4CA',
},
]}
>
{label}
- </Animated.Text>
+ </Text>
</Animated.View>
</View>
)
이제 useNativeDriver: true
를 Animated
에 전달하여 이 드라이버를 사용할 수 있습니다.생성 오류 상태
이것은 우리가 지원해야 할 최종 텍스트 필드 상태입니다.
errorText
라는 새 아이템을 간단하게 정의하고, 이 아이템이 비어 있지 않을 때 탭과 테두리 색을 수정합니다.type Props = React.ComponentProps<typeof TextInput> & {
label: string
+ errorText?: string | null
}
const TextField: React.FC<Props> = (props) => {
const {
label,
+ errorText,
style,
onBlur,
onFocus,
...restOfProps
} = props
+ let color = isFocused ? '#080F9C' : '#B9C4CA'
+ if (errorText) {
+ color = '#B00020'
+ }
+
return (
<View style={style}>
<TextInput
- style={styles.input}
+ style={[
+ styles.input,
+ {
+ borderColor: color,
+ },
+ ]}
{...restOfProps}
onBlur={(event) => {
setIsFocused(false)
@@ -72,13 +83,15 @@ const TextField: React.FC<Props> = (props) => {
style={[
styles.label,
{
- color: isFocused ? '#080F9C' : '#B9C4CA',
+ color,
},
]}
>
{label}
+ {errorText ? '*' : ''}
</Text>
</Animated.View>
+ {!!errorText && <Text style={styles.error}>{errorText}</Text>}
</View>
)
}
const styles = StyleSheet.create({
+ error: {
+ marginTop: 4,
+ marginLeft: 12,
+ fontSize: 12,
+ color: '#B00020',
+ fontFamily: 'Avenir-Medium',
+ },
})
마지막 윤색
TextField는 현재 보기에는 괜찮지만, 몇 가지 작은 문제를 해결해야 한다.
첫 번째 문제는 우리가 입력한 텍스트가
isFocused: false
에서 사라진다는 것이다.따라서 입력한 값이 비어 있지 않을 때 항상 태그를 맨 위에 배치해야 합니다. const {
label,
errorText,
+ value,
style,
onBlur,
onFocus,
...restOfProps
} = props
const [isFocused, setIsFocused] = useState(false)
const focusAnim = useRef(new Animated.Value(0)).current
useEffect(() => {
Animated.timing(focusAnim, {
- toValue: isFocused ? 1 : 0,
+ toValue: isFocused || !!value ? 1 : 0,
duration: 150,
easing: Easing.bezier(0.4, 0, 0.2, 1),
useNativeDriver: true,
}).start()
- }, [focusAnim, isFocused])
+ // make sure you are passing `value` to the dependency array
+ // so the effect will be run anytime the value changes.
+ }, [focusAnim, isFocused, value]
두 번째 문제는 공백으로 입력한 탭을 눌렀을 때입니다.클릭한 요소는 하나Text
가 아니라 하나TextInput
일 뿐이므로 React Native는 키보드를 트리거하지 않습니다.따라서 탭을 단추로 만들고 입력 초점 이벤트를 수동으로 터치해야 합니다.// create an input ref
const inputRef = useRef<TextInput>(null)
// pass `inputRef` to the TextInput
<TextInput ref={inputRef} />
// wrap label View with `TouchableWithoutFeedback`
<TouchableWithoutFeedback onPress={() => inputRef.current?.focus()}>
출력
다음은
TextField
의gif 미리보기입니다.마찬가지로 Github에서 전체 버전을 찾을 수 있습니다.
Reference
이 문제에 관하여(React Native를 사용하여 애니메이션 텍스트 필드 만들기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/halilb/creating-an-animated-textfield-with-react-native-4jh2텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)