我是 React Native 的新手,正在尝试使用react-native-tab-view
. 我想要实现的就像 Twitter Profile UI。
这是我到目前为止所做的:
我想要实现的是当用户向下滚动时,标签栏将粘在动画标题上。当用户再次向上滚动时,标签会慢慢回到之前的位置。
我已经尝试了很多方法来做到这一点,但我无法很好地解决这个问题。
这是代码
import ...
const WIDTH = Dimensions.get('screen').width;
const initialLayout = WIDTH;
const HEADER_MAX_HEIGHT = 120;
const HEADER_MIN_HEIGHT = 70;
const PROFILE_IMAGE_MAX_HEIGHT = 80;
const PROFILE_IMAGE_MIN_HEIGHT = 40;
const imgData = [
// ...
];
const NUM_COLUMNS = 3;
const ProfileScreen = (props) => {
const [index, setIndex] = useState(0);
const [isFollowed, setIsFollowed] = useState(false);
const [enableScrollView, setEnableScrollView] = useState(false);
const [routes] = React.useState([
{ key: 'post', title: 'Posts'},
{ key: 'trip', title: 'Blogs'},
]);
const renderScene = SceneMap({
post: PostView,
trip: TripView,
});
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],
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_MAX_HEIGHT + 5
],
extrapolate: 'clamp'
});
const headerZindex = scrollY.interpolate({
inputRange: [0, HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT, 120],
outputRange: [0, 0, 1000],
extrapolate: 'clamp'
});
const profile = useSelector(state => state.profile.userProfile );
return(
<SafeAreaView style={styles.container}>
<Animated.View style={[styles.backgroundImage, {
height: headerHeight,
zIndex: headerZindex,
elevation: headerZindex, //required for android
}]}>
</Animated.View>
<ScrollView
style={{flex: 1}}
scrollEventThrottle={16}
onScroll={Animated.event(
[{nativeEvent: {contentOffset: {y: scrollY}}}],
)}
bounces={true}
nestedScrollEnabled={true}
>
<View style={{overflow: 'hidden', position: 'absolute', top: HEADER_MIN_HEIGHT/2, left: HEADER_MIN_HEIGHT*2.6}}>
<TouchableOpacity><Icon color='white' name='camera-retro' size={30} /></TouchableOpacity>
</View>
<Animated.View style={[styles.profileImgContainer,{
height: profileImageHeight,
width: profileImageHeight,
marginTop: profileImageMarginTop,
borderRadius: PROFILE_IMAGE_MAX_HEIGHT/2,
}]}>
<Image source={{uri: profile.userAvatar}} style={styles.profileImg} />
</Animated.View>
[...]
<TabView
style={{
flex: 4.2,
}}
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={initialLayout}
renderTabBar={props => {
return(
<Animated.View style={{
position: 'absolute',
top: 0,
left: 0,
right: 0,
zIndex: headerZindex,
elevation: headerZindex,
// transform: [{ translateY: tabBarHeader }],
// marginTop: tabBarHeader
}}>
<TabBar {...props} />
</Animated.View>
)
}}
/>
</ScrollView>
</SafeAreaView>
);
};
const PostView = props => {
return(
<View>
<FlatList
style={{marginTop: 53}}
data={imgData}
numColumns={NUM_COLUMNS}
keyExtractor={item => item.id}
renderItem={(itemData) => (
<TouchableOpacity>
<Image source={itemData.item.source} style={{height:WIDTH/3, width:WIDTH/3, borderColor: 'white', borderWidth: 2}}/>
</TouchableOpacity>
)}
/>
</View>
)
}
const TripView = props => {
return(
<Button title="Trip Display" />
)
}
export default ProfileScreen;
const styles = StyleSheet.create({
//...
})
我还遇到了错误:“虚拟化列表永远不应该嵌套在普通的 ScrollView 中”,并找到了类似ListHeaderComponent
或使用的道具onStartShouldSetResponderCapture
。
但我无法找出适当的进展来实施这些,或者准确地说,我不知道下一步该去哪里
请帮我。非常感谢!