【发布时间】:2020-05-07 11:14:53
【问题描述】:
我正在尝试为View 设置动画,使其在滚动时粘在某个位置。我使用的滚动组件是FlatList。
这是我到目前为止所做的:
基本上,这就是我的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到达标题的底部边框的位置时,follow View 会坚持下去。 (关注View必须与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
请帮助我。我将不胜感激!
【问题讨论】:
-
你能分享一下最终结果吗,我没有推特应用!
-
谢谢奥利弗。我已添加到问题内容中
标签: javascript reactjs react-native animation react-animated