개발자

React-Native BottomSheet 질문

2023년 07월 19일조회 110

안녕하세요 제가 만든 BottomSheet 컴포넌트가 있습니다. BottomSheet를 사용하는 곳에서 closeBottomSheet 을 부르면 애니메이션 없이 닫히는데 closeModal을 호출하고도 애니메이션을 주면서 닫을 수 있는 방법이 있을까요?

1// BottomSheet
2
3import React, { useEffect, useRef } from 'react';
4import {
5  View,
6  StyleSheet,
7  Text,
8  Modal,
9  Animated,
10  TouchableWithoutFeedback,
11  Dimensions,
12  PanResponder,
13} from 'react-native';
14import Color from '../../constants/color';
15import TYPOS from './typo';
16
17interface BottomSheetProps {
18  isOpened: boolean;
19  onClose: () => void;
20  children: React.ReactNode;
21  height: number;
22}
23
24const BottomSheet = ({
25  isOpened,
26  onClose,
27  children,
28  height,
29}: BottomSheetProps) => {
30  const screenHeight = Dimensions.get('screen').height;
31  const panY = useRef(new Animated.Value(screenHeight)).current;
32  const translateY = panY.interpolate({
33    inputRange: [-1, 0, 1],
34    outputRange: [0, 0, 1],
35  });
36
37  const resetBottomSheet = Animated.timing(panY, {
38    toValue: 0,
39    duration: 300,
40    useNativeDriver: true,
41  });
42
43  const closeBottomSheet = Animated.timing(panY, {
44    toValue: screenHeight,
45    duration: 300,
46    useNativeDriver: true,
47  });
48
49  const panResponders = useRef(
50    PanResponder.create({
51      onStartShouldSetPanResponder: () => true,
52      onMoveShouldSetPanResponder: () => false,
53      onPanResponderMove: (event, gestureState) => {
54        panY.setValue(gestureState.dy);
55      },
56      onPanResponderRelease: (event, gestureState) => {
57        if (
58          gestureState.dy > 0 &&
59          (gestureState.dy > 80 || gestureState.vy > 1.5)
60        ) {
61          closeModal();
62        } else {
63          resetBottomSheet.start();
64        }
65      },
66    })
67  ).current;
68
69  useEffect(() => {
70    if (isOpened) {
71      resetBottomSheet.start();
72    }
73  }, [isOpened]);
74
75  const closeModal = () => {
76    closeBottomSheet.start(() => {
77      onClose();
78    });
79  };
80
81  return (
82    <Modal
83      visible={isOpened}
84      animationType="fade"
85      transparent
86      statusBarTranslucent
87    >
88      <TouchableWithoutFeedback onPress={closeModal}>
89        <View style={styles.overlay}>
90          <Animated.View
91            style={[
92              styles.background,
93              {
94                opacity: panY.interpolate({
95                  inputRange: [0, screenHeight],
96                  outputRange: [1, 0],
97                }),
98              },
99            ]}
100          />
101          <Animated.View
102            style={[
103              styles.bottomSheetContainer,
104              {
105                height,
106                transform: [{ translateY: translateY }],
107              },
108            ]}
109            {...panResponders.panHandlers}
110          >
111            <View style={{ alignItems: 'center' }}>
112              <View
113                style={{
114                  width: 44,
115                  height: 4,
116                  backgroundColor: '#DEDEDE',
117                  marginVertical: 8,
118                }}
119              ></View>
120            </View>
121            <View>{children}</View>
122          </Animated.View>
123        </View>
124      </TouchableWithoutFeedback>
125    </Modal>
126  );
127};
128
129const styles = StyleSheet.create({
130  overlay: {
131    flex: 1,
132    justifyContent: 'flex-end',
133    backgroundColor: 'rgba(0, 0, 0, 0.4)',
134  },
135  background: {
136    ...StyleSheet.absoluteFillObject,
137  },
138  bottomSheetContainer: {
139    backgroundColor: Color.white,
140    borderTopLeftRadius: 24,
141    borderTopRightRadius: 24,
142  },
143});
144
145export default BottomSheet;
146
147// 사용하는 곳
148import React, { useState } from 'react';
149import { View, Text } from 'react-native';
150import TYPOS from '../../components/ui/typo';
151import Button from '../../components/ui/buttons/Button';
152import Color from '../../constants/color';
153import BottomSheet from '../../components/ui/BottomSheet';
154
155const Test = () => {
156  const [isVisible, setIsVisible] = useState(false);
157
158  const openBottomSheet = () => {
159    setIsVisible(true);
160  };
161
162  const closeBottomSheet = () => {
163    setIsVisible(false);
164  };
165
166  return (
167    <>
168      <View>
169        <Button label="BottomSheet" onPressHandler={openBottomSheet} />
170      </View>
171      <BottomSheet
172        isOpened={isVisible}
173        onClose={() => {
174          closeBottomSheet();
175        }}
176        height={310}
177      >
178        <View
179          style={{
180            width: '100%',
181            height: '100%',
182          }}
183        >
184          <View style={{ paddingVertical: 24, paddingHorizontal: 16 }}>
185            <Text style={[TYPOS.headline3, { color: Color.black }]}>
186              바텀 시트 내용
187            </Text>
188          </View>
189          <View style={{ marginHorizontal: 16, marginTop: 24 }}>
190            <Button label="닫기" onPressHandler={closeBottomSheet} />
191          </View>
192        </View>
193      </BottomSheet>
194    </>
195  );
196};
197export default Test;
이 질문이 도움이 되었나요?
'추천해요' 버튼을 누르면 좋은 질문이 더 많은 사람에게 노출될 수 있어요. '보충이 필요해요' 버튼을 누르면 질문자에게 질문 내용 보충을 요청하는 알림이 가요.

답변 0

답변이 없을 때 보이는 아이콘

첫 답변을 기다리고 있어요.

지금 가입하면 모든 질문의 답변을 볼 수 있어요!

현직자들의 명쾌한 답변을 얻을 수 있어요.

또는

이미 회원이신가요?

목록으로

지금 가입하면 모든 질문의 답변을 볼 수 있어요!