Я пытаюсь анимировать View
, чтобы он был привязан к определенной позиции при прокрутке. Компонент прокрутки, который я использую: FlatList
.
Вот что я до сих пор сделал:
ProfileScreen
По сути, это структура моего ProfileScreen.js
:
<View>
<Animated.View /> --> The animated cover photo (includes the animated avatar as well)
<Animated.FlatList /> --> This renders the list of images you see in the video. And a ListHeaderComponent renders the content from the name to the View of follow numbers
</View
Теперь я хочу достичь:
, когда пользователь прокручивает весь FlatList
и достигает положения, в котором View
, содержащий последующие числа , достигает нижней границы заголовка , что следовать View
будет там. (следующий View
должен прокручиваться вместе с th FlatList
)
Точно так же, как вид с липкой вкладкой.
Вы можете представить, что мой экран «цели» похож на экран профиля Twitter.
Вот полный код:
//...
const ProfileScreen = (props) => {
const [index, setIndex] = useState(0);
const [isFollowed, setIsFollowed] = useState(false);
const [firstFollow, setFirstFollow] = useState(false);
const [enableScrollView, setEnableScrollView] = useState(false);
const trips = useSelector(state => state.trips.availableTrips);
const scrollY = useRef(new Animated.Value(0)).current;
const headerHeight = scrollY.interpolate({
inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT], // [0, 50]
outputRange: [HEADER_MAX_HEIGHT, HEADER_MIN_HEIGHT], // [120,70]
extrapolate: 'clamp'
});
const profileImageHeight = scrollY.interpolate({
inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT-10],
outputRange: [PROFILE_IMAGE_MAX_HEIGHT,PROFILE_IMAGE_MIN_HEIGHT],
extrapolate: 'clamp'
});
const profileImageMarginTop = scrollY.interpolate({
inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT],
outputRange: [
HEADER_MAX_HEIGHT - PROFILE_IMAGE_MAX_HEIGHT / 2,
HEADER_MIN_HEIGHT /2 - PROFILE_IMAGE_MIN_HEIGHT/2
],
extrapolate: 'clamp'
});
const profileImageMarginLeft = scrollY.interpolate({
inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT],
outputRange: [
WIDTH/2 - PROFILE_IMAGE_MAX_HEIGHT/2,
10
],
extrapolate: 'clamp'
})
const headerZindex = scrollY.interpolate({
inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT, 120],
outputRange: [0, 0, 1000],
extrapolate: 'clamp'
});
const headerTitleColor = scrollY.interpolate({
inputRange: [0, 50, 70, 80, 90, 100],
outputRange: ['transparent', 'transparent', 'transparent', 'transparent', 'transparent', 'white'],
extrapolate: 'extend'
});
const tabBarPosition = scrollY.interpolate({
inputRange: [0,200],
outputRange: [(HEADER_MAX_HEIGHT + PROFILE_IMAGE_MIN_HEIGHT)*2, HEADER_MIN_HEIGHT],
extrapolate: 'clamp'
})
const _render_Sitcky_Info_View = () => {
return(
<View style={{marginTop: HEADER_MAX_HEIGHT+ PROFILE_IMAGE_MAX_HEIGHT/2}}>
<InfoDisplay
isFollowed={isFollowed}
onFollow={onFollow}
userName={profile.userName}
tripsNumber={trips.length}
navigateToTripsListScreen={() => props.navigation.navigate('TripsListScreen')}
/>
</View>
)
}
return(
<SafeAreaView style={styles.container}>
<Animated.View style={[styles.backgroundImage, {
height: headerHeight,
zIndex: headerZindex,
elevation: headerZindex,
justifyContent: 'center'
}]}>
<Animated.Image
source={require('../../../assets/images/beach.jpg')}
style={{
flex: 1,
}}
/>
<View style={{
position: 'absolute',
left: 60,
}}>
<Animated.Text style={{color: headerTitleColor, fontSize: 20, fontWeight: 'bold'}}>{profile.userName}</Animated.Text>
</View>
<Animated.View style={[styles.profileImgContainer,{
height: profileImageHeight,
width: profileImageHeight,
borderRadius: PROFILE_IMAGE_MAX_HEIGHT/2,
position: 'absolute',
top: profileImageMarginTop,
left: profileImageMarginLeft,
alignSelf: 'center'
}]}>
<Image source={{uri: profile.userAvatar}} style={styles.profileImg} />
</Animated.View>
</Animated.View>
<Animated.FlatList
style={{flex: 1, backgroundColor: 'transparent'}}
contentContainerStyle={{justifyContent: 'center', alignItems: 'center'}}
scrollEventThrottle={16}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: scrollY}}}],
)}
showsVerticalScrollIndicator={false}
bounces={true}
data={imgData}
numColumns={NUM_COLUMNS}
keyExtractor={item => item.id}
renderItem={(itemData) => {
return(
<TouchableOpacity style={{
marginHorizontal: findMidOfThree(imgData, itemData.index) ? 0 : 5 ,
marginVertical: 3,
shadowColor: 'black',
shadowOpacity: 0.26,
shadowOffset: { width: 0, height: 2 },
shadowRadius: 8,
elevation: 7,
}}>
<Image source={itemData.item.source} style={{
height: WIDTH/3.5,
width: WIDTH/3.5,
borderColor: 'white',
borderWidth: 2,
borderRadius: 10,
}} />
</TouchableOpacity>
)
}}
ListHeaderComponent={_render_Sitcky_Info_View}
{...props}
>
</Animated.FlatList>
</SafeAreaView>
);
};
export default ProfileScreen;
const styles = StyleSheet.create({
//...
})
InfoDisplay
содержит следующие числа View
. Я знаю, что мне придется отделить его и перенести в ProfileScreen
. Я также попытался оживить его с помощью tabBarPosition
, но это не сработало, как я думал
Вы можете представить, что мой экран «цели» похож на экран профиля Twitter, на котором мои подписаны числа View
действует как вкладка в Twitter
https://drive.google.com/file/d/1ERt_7gnPgiwXPg-WODXSnrOZ10kQgPQl/view?usp=drivesdk
ПОМОГИТЕ мне, пожалуйста. Буду очень признателен!