- 父组件中主要的工作使用的是动画Animated,对
Animated.ScrollView
组件的onScroll
监听滚动位置,使用Animated.event
将偏移量保存import React, {useRef} from 'react';
import {
Text,
View,
ScrollView,
Animated,
} from 'react-native';
import EveryDayRecommend from '@/view/DailyRecommendation/components/EveryDayRecommend/EveryDayRecommend';
import StickyHeader from '@/view/DailyRecommendation/components/StickyHeader/StickyHeader';
const DefaultRecommend = () => {
let scrollY = useRef(new Animated.Value(0)).current;
let headHeight = useRef(200).current;
return (
<View>
<Animated.ScrollView
onScroll={
Animated.event(
[
{
nativeEvent: {contentOffset: {y: scrollY}},
},
],
{useNativeDriver: true},
)
}>
{}
<StickyHeader
stickyHeaderY={headHeight}
stickyScrollY={scrollY}
>
<EveryDayRecommend
source={require('@/assets/image/defaultRecommendGif.gif')}
descBottom="默认的推荐风格"
stickyHeaderY={headHeight}
stickyScrollY={scrollY}
/>
<Text>全部播放</Text>
</StickyHeader>
<ScrollView>
<View>
{[...Array.from({length: 100}).keys()].map(item => {
return <Text key={item}>{item}</Text>;
})}
</View>
</ScrollView>
</Animated.ScrollView>
</View>
);
};
export default DefaultRecommend;
- 子组件
StickyHeader
中接受了父组件传递的,
stickyHeaderY = null
吸顶位置stickyScrollY
偏移量children
传递内容-
style
样式 - 在组件中使用了
Animated.View
属性onLayout
初始化会返回当前元素的属性,这里使用的情况是防止未传递吸顶高度,所以默认将其偏移量量作为吸顶高度
import React from 'react';
import {StyleSheet, Animated} from 'react-native';
import {useRef} from 'react';
const StickyHeader = ({
stickyHeaderY = null,
stickyScrollY,
children,
style,
}) => {
let stickyLayoutY = useRef(0).current;
let y = stickyHeaderY !== null ? stickyHeaderY : stickyLayoutY;
const _onLayout = event => {
stickyLayoutY = event.nativeEvent.layout.y;
};
return (
<Animated.View
onLayout={_onLayout}
style={[
style,
styles.container,
{
transform: [
{
translateY: stickyScrollY.interpolate({
inputRange: [-1, 0, y, y + 1],
outputRange: [0, 0, 0, 1],
}),
},
],
},
]}>
{children}
</Animated.View>
);
};
export default React.memo(StickyHeader);
const styles = StyleSheet.create({
container: {
zIndex: 100,
},
});
- 并且使其组件中的图片会跟随,滚动逐渐隐藏,在父组件中有一个
EveryDayRecommend
这里面是一个图片和日期
- 主要部分就是通过最后一个
Animated.View
进行定位和整个元素重合,设置其透明度,透明度结束位置,就是触顶位置
import React, {useRef, useState} from 'react';
import {
Dimensions,
Image,
StyleSheet,
Text,
View,
Animated,
} from 'react-native';
import {useTheme} from '@react-navigation/native';
const widthWindow = Dimensions.get('window').width;
const EveryDayRecommend = props => {
const {otherColor, fontSize} = useTheme();
const [currentDate] = useState(new Date());
return (
<View style={styles.everyDayRecommend}>
<Image
style={[{width: widthWindow, height: 300}]}
source={props.source}
/>
<View style={styles.desc}>
<Text style={[styles.dateCurrent, {color: otherColor.bgFontColor}]}>
<Text style={{fontSize: fontSize.superBig}}>
{currentDate.getDate() >= 10
? currentDate.getDate()
: '0' + currentDate.getDate()}
</Text>
<Text style={{fontSize: fontSize.small}}>
/
{currentDate.getMonth() >= 10
? currentDate.getMonth() + 1
: '0' + (currentDate.getDay() + 1)}
</Text>
</Text>
<Text style={{color: otherColor.bgFontColor}}>{props.descBottom}</Text>
</View>
{
}
<Animated.View
style={[
styles.shelter,
{
opacity: props.stickyScrollY?.interpolate({
inputRange: [0, props.stickyHeaderY],
outputRange: [0, 1],
}),
},
]}
/>
</View>
);
};
export default EveryDayRecommend;
const styles = StyleSheet.create({
everyDayRecommend: {
position: 'relative',
},
desc: {
position: 'absolute',
bottom: 20,
paddingHorizontal: 10,
},
dateCurrent: {
flexDirection: 'row',
fontWeight: 'bold',
},
shelter: {
width: widthWindow,
height: 300,
zIndex: 100,
position: 'absolute',
top: 0,
left: 0,
backgroundColor: '#fff',
},
});