|
|
@@ -11,6 +11,7 @@ import {
|
|
|
PanResponder,
|
|
|
ScrollView,
|
|
|
Animated,
|
|
|
+ Easing,
|
|
|
View
|
|
|
} from "react-native";
|
|
|
import type IBottomSheetProps from "./type";
|
|
|
@@ -46,6 +47,7 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
|
|
|
isWrapSafeAreaContext = true,
|
|
|
backgroundColor = "default",
|
|
|
isWorkAsFullScreen = false,
|
|
|
+ scrollEndThreshold = 0.85,
|
|
|
isWorkWithPortal = true,
|
|
|
isCloseOnOverlay = true,
|
|
|
handleContainerSpacing,
|
|
|
@@ -58,6 +60,7 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
|
|
|
onOverlayPressed,
|
|
|
scrollViewProps,
|
|
|
scrollViewStyle,
|
|
|
+ onScrollEnd,
|
|
|
customTheme,
|
|
|
modalProps,
|
|
|
snapPoint,
|
|
|
@@ -267,6 +270,20 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
|
|
|
};
|
|
|
}, []);
|
|
|
|
|
|
+ const checkScrollThreshold = () => {
|
|
|
+ if (!onScrollEnd) return;
|
|
|
+
|
|
|
+ const maxS = Math.max(0, scrollViewContentHeight.current - scrollViewLayoutHeight.current);
|
|
|
+
|
|
|
+ if (maxS <= 0) return;
|
|
|
+
|
|
|
+ const ratio = scrollOffset.current / maxS;
|
|
|
+
|
|
|
+ if (ratio >= scrollEndThreshold) {
|
|
|
+ onScrollEnd();
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
const openAnimation = () => {
|
|
|
resetState();
|
|
|
|
|
|
@@ -345,7 +362,21 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
|
|
|
const panResponder = useRef(
|
|
|
PanResponder.create({
|
|
|
onStartShouldSetPanResponder: () => false,
|
|
|
- onMoveShouldSetPanResponderCapture: () => isCanSwipeRef.current ? true : false,
|
|
|
+ onMoveShouldSetPanResponderCapture: (_, gestureState) => {
|
|
|
+ const {
|
|
|
+ dy
|
|
|
+ } = gestureState;
|
|
|
+
|
|
|
+ if(!isCanSwipeRef.current) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(Math.abs(dy) < 20) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ return true;
|
|
|
+ },
|
|
|
onPanResponderGrant: (evt) => {
|
|
|
if(!isCanSwipeRef.current) return;
|
|
|
|
|
|
@@ -512,6 +543,41 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
|
|
|
const isFromTopArea = gestureStartY.current < TOP_GRAB_AREA;
|
|
|
|
|
|
if (isAtTavan && hasScroll && !isAtTop && !isFromTopArea) {
|
|
|
+ const velocity = gestureState.vy;
|
|
|
+
|
|
|
+ if (Math.abs(velocity) > 0.1) {
|
|
|
+ const momentum = velocity * -350;
|
|
|
+
|
|
|
+ const maxS = Math.max(0, scrollViewContentHeight.current - scrollViewLayoutHeight.current);
|
|
|
+
|
|
|
+ const targetOffset = Math.max(0, Math.min(scrollOffset.current + momentum, maxS));
|
|
|
+
|
|
|
+ const scrollAnim = new Animated.Value(scrollOffset.current);
|
|
|
+
|
|
|
+ scrollAnim.addListener(({
|
|
|
+ value
|
|
|
+ }) => {
|
|
|
+ scrollViewRef.current?.scrollTo({
|
|
|
+ y: value,
|
|
|
+ animated: false
|
|
|
+ });
|
|
|
+ scrollOffset.current = value;
|
|
|
+ });
|
|
|
+
|
|
|
+ Animated.timing(scrollAnim, {
|
|
|
+ duration: Math.min(Math.abs(momentum) * 2, 600),
|
|
|
+ easing: Easing.out(Easing.quad),
|
|
|
+ useNativeDriver: false,
|
|
|
+ toValue: targetOffset
|
|
|
+ }).start(() => {
|
|
|
+ scrollAnim.removeAllListeners();
|
|
|
+
|
|
|
+ checkScrollThreshold();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ checkScrollThreshold();
|
|
|
+ }
|
|
|
+
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -535,7 +601,7 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
|
|
|
const isFastSwipeDown = gestureState.vy > 0.5;
|
|
|
const isFastSwipeUp = gestureState.vy < -0.5;
|
|
|
|
|
|
- if (currentT <= 0.5) {
|
|
|
+ if (currentT <= Math.max(pivot * 0.2, 60)) {
|
|
|
let toValue = pivot;
|
|
|
|
|
|
if (isFastSwipeUp && isCanFullScreenOnSwipeRef.current) {
|
|
|
@@ -565,6 +631,13 @@ const BottomSheet: RefForwardingComponent<IBottomSheetRef, IBottomSheetProps> =
|
|
|
friction: 10,
|
|
|
tension: 40
|
|
|
}).start();
|
|
|
+
|
|
|
+ Animated.spring(animatedTranslateY, {
|
|
|
+ useNativeDriver: false,
|
|
|
+ toValue: 0,
|
|
|
+ friction: 10,
|
|
|
+ tension: 40
|
|
|
+ }).start();
|
|
|
} else {
|
|
|
let toValueT = 0;
|
|
|
let isClosing = false;
|