import React, { useEffect, useRef } from 'react';
import {
View,
Text,
StyleSheet,
TouchableOpacity,
Animated,
Dimensions,
Image,
PanResponder,
} from 'react-native';
const { height: screenHeight } = Dimensions.get('window');
const MIN_HEIGHT = 100;
const INITIAL_TRANSLATE_Y = screenHeight - MIN_HEIGHT;
const ScreenDemo = ({ navigation, theme }) => {
const slideAnim = useRef(new Animated.Value(INITIAL_TRANSLATE_Y)).current;
const currentPosition = useRef(INITIAL_TRANSLATE_Y);
const isVisible = useRef(true);
const CLOSED_POSITION = INITIAL_TRANSLATE_Y; // уровень открытия экрана
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: (_, gestureState) => {
return Math.abs(gestureState.dy) > 10;
},
onPanResponderGrant: () => {
slideAnim.stopAnimation(); // Останавливаем любую анимацию при касании
},
onPanResponderMove: (_, gestureState) => {
const newPosition = Math.max(
0,
Math.min(
currentPosition.current + gestureState.dy,
CLOSED_POSITION
)
);
slideAnim.setValue(newPosition);
},
onPanResponderRelease: (_, gestureState) => {
currentPosition.current = slideAnim._value;
},
})
).current;
// Начальное появление
useEffect(() => {
slideAnim.setValue(screenHeight);
Animated.timing(slideAnim, {
toValue: INITIAL_TRANSLATE_Y,
duration: 350,
useNativeDriver: true,
}).start();
}, []);
return (
<View style={styles.container}>
<View style={styles.backgroundContent}>
<Text style={styles.demoTitle}>Основной экран</Text>
<Text style={{marginBottom:440}}>1 материалы</Text>
<Text style={{marginBottom:440}}>2 материалы</Text>
<Text style={{marginBottom:440}}>4 материалы</Text>
<Text style={{marginBottom:440}}>5 материалы</Text>
</View>
<Animated.View
style={[
styles.overlay,
{
backgroundColor: theme === 'dark' ? '#1d2632' : '#ffffff',
transform: [{ translateY: slideAnim }],
},
]}
{...panResponder.panHandlers}
>
<View style={styles.handleContainer}>
<View style={styles.handle} />
</View>
<View style={styles.content}>
<Text style={[styles.title, theme === 'dark' && styles.textDark]}>
Панель
</Text>
<Text style={{marginBottom:440}}>1 материалы</Text>
<Text style={{marginBottom:440}}>2 материалы</Text>
<Text style={{marginBottom:440}}>4 материалы</Text>
<Text style={{marginBottom:440}}>5 материалы</Text>
</View>
</Animated.View>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#f0f2f5' },
backgroundContent: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 40,
},
demoTitle: { fontSize: 28, fontWeight: 'bold', color: '#333', marginBottom: 8 },
demoSubtitle: { fontSize: 16, color: '#666', textAlign: 'center' },
handleContainer: { alignItems: 'center', paddingVertical: 12 },
handle: {
width: 42,
height: 5,
backgroundColor: '#ccc',
borderRadius: 999,
},
title: { fontSize: 24, fontWeight: '700', marginBottom: 24, color: '#222' },
section: { marginBottom: 12, padding: 18, borderRadius: 12 },
sectionText: { fontSize: 16, fontWeight: '600', color: '#333' },
infoText: { fontSize: 13, color: '#666', textAlign: 'center' },
textDark: { color: '#fff' },
});
export default ScreenDemo;import React, { useEffect, useRef, useState } from 'react';
import {
View, Text, StyleSheet, Animated, Dimensions,
PanResponder, ScrollView
} from 'react-native';
const { height: screenHeight } = Dimensions.get('window');
const MIN_HEIGHT = 70;
const MAX_HEIGHT = 3400; // потолок, выше которого панель не открывать, даже если контент больше
const INITIAL_TRANSLATE_Y = screenHeight - MIN_HEIGHT;
const CLOSED_POSITION = INITIAL_TRANSLATE_Y;
const ScreenDemo1 = ({ navigation }) => {
const slideAnim = useRef(new Animated.Value(INITIAL_TRANSLATE_Y)).current;
const currentPosition = useRef(INITIAL_TRANSLATE_Y);
const [btmCoo, setBtmCoo] = useState(100);
const [isAtTop, setIsAtTop] = useState(false);
const [scrollY, setScrollY] = useState(0);
const [heightBottom, setHeightBottom] = useState(0);
// ref с тем же значением — он НЕ застывает в замыканиях PanResponder
const contentHeightRef = useRef(0);
const measuredRef = useRef(false); // флаг "высота уже посчитана"
// актуальный верхний предел открытия панели
const getMaxOpenPosition = () => {
const measuredHeight = measuredRef.current
? contentHeightRef.current // + MIN_HEIGHT + 70 на хэндл сверху контента
: MAX_HEIGHT; // пока высота не измерена — используем потолок как дефолт
const h = Math.min(measuredHeight, MAX_HEIGHT);
return screenHeight - h;
};
const updateIsAtTop = (value) => {
const newIsAtTop = Math.abs(value - getMaxOpenPosition()) < 15;
if (newIsAtTop !== isAtTop) {
setIsAtTop(newIsAtTop);
}
};
const panResponder = useRef(
PanResponder.create({
onMoveShouldSetPanResponder: (_, gestureState) => {
if (isAtTop && gestureState.dy > 0 && scrollY <= 5) {
return true;
}
return Math.abs(gestureState.dy) > 10;
},
onPanResponderGrant: () => {
slideAnim.stopAnimation();
},
onPanResponderMove: (_, gestureState) => {
let newPosition = currentPosition.current + gestureState.dy;
const maxOpenPosition = getMaxOpenPosition(); // читаем актуальное значение из ref
newPosition = Math.max(
maxOpenPosition,
Math.min(newPosition, CLOSED_POSITION)
);
setBtmCoo(newPosition);
//console.log('translateY:', newPosition);
slideAnim.setValue(newPosition);
updateIsAtTop(newPosition);
},
onPanResponderRelease: () => {
const finalPosition = slideAnim._value;
currentPosition.current = finalPosition;
updateIsAtTop(finalPosition);
},
})
).current;
useEffect(() => {
slideAnim.setValue(screenHeight);
Animated.timing(slideAnim, {
toValue: INITIAL_TRANSLATE_Y,
duration: 350,
useNativeDriver: true,
}).start(() => {
updateIsAtTop(INITIAL_TRANSLATE_Y);
});
}, []);
return (
<View style={styles.container}>
<View style={styles.backgroundContent}>
<Text style={styles.demoTitle}>Основной экран</Text>
<Text style={{ marginBottom: 240 }}>1 материалы</Text>
<Text style={{ marginBottom: 240 }}>2 материалы</Text>
<Text style={{ marginBottom: 240 }}>3 материалы</Text>
<Text style={{ marginBottom: 240 }}>4 материалы</Text>
<Text style={{ marginBottom: 240 }}>5 материалы</Text>
<Text style={{ }}>3 материалы</Text>
</View>
{btmCoo < 0 && (
<View style={{backgroundColor: '#529b00',position: 'absolute',top: 0,left: 0,right: 0,zIndex: 1999, }}>
<Text style={[styles.title, { color: '#fff' }]}>
Панель {btmCoo}
</Text>
</View>
)}
<Animated.View
style={[
styles.overlay,
{
backgroundColor: '#000',
transform: [{ translateY: slideAnim }],
},
]}
{...panResponder.panHandlers}
>
<View
onLayout={(e) => {
const { height } = e.nativeEvent.layout;
contentHeightRef.current = height; // используется внутри PanResponder
measuredRef.current = true;
setHeightBottom(height); // для рендера/отображения, если нужно
console.log('CONTENT HEIGHT:', height);
}}
style={{ backgroundColor: '#000' }}>
{btmCoo > 0 && (
<View style={{backgroundColor: '#580597',position: 'absolute',top: 0,left: 0,right: 0,zIndex: 1999, }}>
<Text style={[styles.title, { color: '#fff' }]}>
Панель {btmCoo}
</Text>
</View>
)}
<View style={{paddingBottom:20}}>
<Text style={{ marginBottom: 240, color: '#fff' }}>Вытягиваемый контент</Text>
<Text style={{ color: '#fff' }}>1 материалы</Text>
<Text style={{ color: '#fff',marginBottom: 240 }}>3 материалы</Text>
<Text style={{ color: '#fff',marginBottom: 240 }}>4 материалы</Text>
<Text style={{ color: '#fff',marginBottom: 240 }}>5 материалы</Text>
<Text style={{ color: '#fff',marginBottom: 240 }}>3 материалы</Text>
<Text style={{ color: '#fff',marginBottom: 240 }}>4 материалы</Text>
<Text style={{ color: '#fff',marginBottom: 240 }}>5 материалы</Text>
</View>
</View>
</Animated.View>
</View>
);
};
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: '#f0f2f5' },
backgroundContent: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 40,
},
demoTitle: { fontSize: 28, fontWeight: 'bold', color: '#333', marginBottom: 8 },
overlay: {
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
height: screenHeight,
borderTopLeftRadius: 24,
borderTopRightRadius: 24,
shadowColor: '#000',
shadowOffset: { width: 0, height: -6 },
shadowOpacity: 0.3,
shadowRadius: 16,
elevation: 25,
zIndex: 1000,
},
handleContainer: { alignItems: 'center', paddingVertical: 12 },
handle: {
width: 42,
height: 5,
backgroundColor: '#ccc',
borderRadius: 999,
},
title: { fontSize: 24, fontWeight: '700', marginBottom: 24 },
});
export default ScreenDemo1;