Layout
export default function FlipCard() {
const { width } = useWindowDimensions();
const cardLayout = useMemo(() => {
return {
position: 'absolute',
backfaceVisibility: 'hidden',
alignItems: 'center',
justifyContent: 'center',
width: width - 48,
height: 300,
borderWidth: 2,
borderRadius: 16,
borderColor: '#eee',
};
}, []);
return <View />
}
View 두개를 absolute로 겹친 후 interpolate를 사용해 서로 반대로 회전 시키면 된다. 이 때 뒷면이 보이지 않도록 backfaceVisibility 속성이 필요하다. chat GPT 피셜로
backfaceVisibility는 3D 공간에서 요소의 뒷면이 어떻게 표시되는지를 정의하는 CSS 속성입니다.
라고 하는데 여러 요소가 겹친 상태에서 뒤에 있는 요소를 보여줄지 숨길지 정할 수 있다!
Animated.View
애니메이션을 걸려면 View대신 Animated.View 로 감싸야한다.
<View style={styles.container}>
<Animated.View style={[styles.front, cardLayout, frontAnimatedStyle]}>
<Text style={styles.cardText}> {`>Front<`}</Text>
</Animated.View>
<Animated.View style={[styles.back, cardLayout, backAnimatedStyle]}>
<Text style={styles.cardText}> {`>Back<`}</Text>
</Animated.View>
</View>
const styles = StyleSheet.create({
container: {
alignItems: 'center',
height: 300,
},
cardText: {
fontSize: 22,
color: '#fff',
},
front: {
backgroundColor: '#cfb4b4',
},
back: {
backgroundColor: '#89c296',
},
});
나중에 만들어진 Back카드가 Front 카드위에 딱맞게 겹쳐서 그려진다.
useAnimatedStyle
이제 카드를 뒤집는 애니메이션을 만들어 Y축을 기준으로 180도씩 회전시키면된다. 앞면은 0도에서 180도로, 뒷면은 180도에서 360도로 뒤집는다.
const spin = useSharedValue(0);
회전각을 정해줄 sharedValue를 만들어준다.
const frontAnimatedStyle = useAnimatedStyle(() => {
const spinValue = interpolate(spin.value, [0, 1], [0, 180]);
return {
transform: [
{
rotateY: withTiming(`${spinValue}deg`, { duration: 300 }),
},
],
};
}, []);
먼저 앞면에 줄 애니메이션 스타일로 0 혹은 1이 들어갈때마다 0~180도를 오가도록 interpolate를 만들어준다. 똑같이 뒷면도
const backAnimatedStyle = useAnimatedStyle(() => {
const spinValue = interpolate(spin.value, [0, 1], [180, 360]);
return {
transform: [
{
rotateY: withTiming(`${spinValue}deg`, { duration: 300 }),
},
],
};
}, []);
180~360도로 바뀌게 만들어둔다.
Trigger
const onFlip = () => {
spin.value = spin.value ? 0 : 1;
};
spin.value는 0 과 1을 return 하도록 하고 트리거에 걸어두면 된다.
<View style={styles.container}>
<Pressable style={[styles.fakeView, cardLayout]} onPress={onFlip} />
<Animated.View style={[styles.front, cardLayout, frontAnimatedStyle]}>
<Text style={styles.cardText}> {`>Front<`}</Text>
</Animated.View>
<Animated.View style={[styles.back, cardLayout, backAnimatedStyle]}>
<Text style={styles.cardText}> {`>Back<`}</Text>
</Animated.View>
</View>
<View>
<Button title="onFlip" onPress={onFlip} />
</View>
버튼을 누르는 것 외에 카드를 눌러도 onFlip이 되도록 만들려면 카드위에 똑같은 크기로 Pressable을 덮어두고 동작하게 하면된다.
<View style={styles.container}>
<Pressable style={[{zIndex:10}, cardLayout]} onPress={onFlip} />
<Animated.View style={[styles.front, cardLayout, frontAnimatedStyle]}>
<Text style={styles.cardText}> {`>Front<`}</Text>
</Animated.View>
<Animated.View style={[styles.back, cardLayout, backAnimatedStyle]}>
<Text style={styles.cardText}> {`>Back<`}</Text>
</Animated.View>
</View>
code
import React, { useMemo } from 'react';
import {
Button,
Pressable,
SafeAreaView,
StyleSheet,
Text,
View,
useWindowDimensions,
} from 'react-native';
import Animated, {
interpolate,
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated';
export default function FlipCard() {
const { width } = useWindowDimensions();
const spin = useSharedValue(0);
const cardLayout = useMemo(() => {
return {
position: 'absolute',
backfaceVisibility: 'hidden',
alignItems: 'center',
justifyContent: 'center',
width: width - 48,
height: 300,
borderWidth: 2,
borderRadius: 16,
borderColor: '#eee',
};
}, []);
const onFlip = () => {
spin.value = spin.value ? 0 : 1;
};
const frontAnimatedStyle = useAnimatedStyle(() => {
const spinValue = interpolate(spin.value, [0, 1], [0, 180]);
return {
transform: [
{
rotateY: withTiming(`${spinValue}deg`, { duration: 300 }),
},
],
};
}, []);
const backAnimatedStyle = useAnimatedStyle(() => {
const spinValue = interpolate(spin.value, [0, 1], [180, 360]);
return {
transform: [
{
rotateY: withTiming(`${spinValue}deg`, { duration: 300 }),
},
],
};
}, []);
return (
<SafeAreaView>
<View style={{ height: 50 }} />
<View style={styles.container}>
<Pressable style={[{ zIndex: 10 }, cardLayout]} onPress={onFlip} />
<Animated.View style={[styles.front, cardLayout, frontAnimatedStyle]}>
<Text style={styles.cardText}> {`>Front<`}</Text>
</Animated.View>
<Animated.View style={[styles.back, cardLayout, backAnimatedStyle]}>
<Text style={styles.cardText}> {`>Back<`}</Text>
</Animated.View>
</View>
<View>
<Button title="onFlip" onPress={onFlip} />
</View>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
alignItems: 'center',
height: 300,
},
cardText: {
fontSize: 22,
color: '#fff',
},
front: {
backgroundColor: '#cfb4b4',
},
back: {
backgroundColor: '#89c296',
},
fakeView: {
zIndex: 100,
},
});
'Programing > React-Native' 카테고리의 다른 글
[RN] 인생이 지루하다면 react-native upgrade (0.66.3 -> 0.72.5) (1) | 2023.11.15 |
---|---|
[RN] Node 업그레이드 후 빌드 오류 (error:0308010C:digital) (0) | 2023.09.11 |
[RN] 플로팅 버튼 아래로 스크롤 할 때만 나오게 구현(feat. reanimated) (0) | 2023.07.05 |
[RN] zsh에서 gradle 에 맞는 Java 버전변경 ( version 62 ) (0) | 2023.01.27 |
[RN] android 빌드 실패 No connected devices (0) | 2023.01.22 |