RN-实现抖音切换视频效果
2020-09-29 本文已影响0人
精神病患者link常
实现效果实现效果
1、上下滑动切换
2、 左滑动弹出列表,右滑动取消列表
3、加载更多、刷新
思路
<View 手势(上滑动、下滑动、左滑动(弹出RightView))>
<FlatList 禁止滚动 renderItem的高和宽和屏幕一样/>
</View>
<RightView 手势(只判断右滑动,用于返回)/>
通过滑动的x、y判断是上下滑动还是左右滑动
1.上下滑动
滑动中:通过 flatList.scrollToOffset
设置flatList
的滚动位置
滑动结束:通过判断滑动的位置 || 滑动的速度 进行切换页面
- 滑动距离超过屏幕的1/4
- 滑动结束时间 - 滑动开始时间 < 300 && 滑动距离超过 50
再通过flatList.scrollToIndex
设置flatList
的滚动位置
2.左滑动,显示RightView
.RightView
position:'absolute'
left: screenWidth
左滑动过程中setState
left
的值,配合LayoutAnimation
实现RightView
弹出
3.下拉刷新,上拉加载
下拉刷新:第一页的位置,下拉超过30
,则显示ListHeaderComponent
上拉加载:最后一页的位置,上拉超过30
,则显示loadingComponent
RightView 手势
右滑动:setState
left
的值,配合LayoutAnimation
实现RightView
消失
具体代码
index.,js
class ClassName extends React.Component {
constructor(props) {
super(props);
this.props.navigation.setOptions({ headerShown: false })
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true)
}
this.state = {
list: [1,2,3,4,5],
cityListLeft:width,
refreshing:false,
isLoading:false
}
this.startTime = 0 // 滚动开始时间
this.endTime = 0 // 滚动结束时间 ,用于计算滚动的速度(结束时间 - 开始时间,时间越短,滚动速度越快)
this.page = 0 // 当前页
// 根据滑动的距离 或者 滑动的速度且滑动距离大于50,判断是否切换
// 纵向滚动切换页面条件一
this.minSlideDistance = 50 // 滚动时间符合要求时的最小滚动距离
this.maxSlideTime = 300 // 最大的滚动时间,
// 条件二
this.minSlideScaleY = 1 / 4 // 最小的滚动比例
// 横向滚动显示页面
this.minSlideScaleX = 1 / 4 // 最小的拖动比例
this.isRefresh = false // 用于判断是否处于下拉刷新中,减少下拉过程总多次setState
this.isLoadMore = false // 用于判断是否处于上拉加载更多中,减少上拉过程总多次setState
// 初始化时定义,不要在render中定义
this.viewabilityConfig = {viewAreaCoveragePercentThreshold: 80}
this.panResponder = PanResponder.create({
// 要求成为响应者,防止弹出的页面中按钮不可点击
onStartShouldSetPanResponder: (evt, gestureState) => {
if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
return true
}
return false
},
onStartShouldSetPanResponderCapture: (evt, gestureState) => {
if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
return true
}
return false
},
onMoveShouldSetPanResponder: (evt, gestureState) => {
if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
return true
}
return false
},
onMoveShouldSetPanResponderCapture: (evt, gestureState) => {
if (Math.abs(gestureState.dx) > 10 || Math.abs(gestureState.dy) > 10){
return true
}
return false
},
onPanResponderGrant: (evt, gestureState) => {
// 滑动开始时间
this.startTime = evt.nativeEvent.timestamp
},
onPanResponderMove: (evt, gestureState) => {
let y = gestureState.dy
let x = gestureState.dx
if (Math.abs(x) > Math.abs(y)){
// 横向滑动
if (Math.abs(x) > 30){
if (x > 0){
// 向右
if (this.state.cityListLeft == width){
}else {
this.setState({
cityListLeft:x
})
}
}else {
// 向左
if (this.state.cityListLeft == 0){
}else {
x = Math.abs(x)
this.setState({
cityListLeft:width - x
})
}
}
}
}else {
// 纵向滑动
if (Math.abs(y) > 30){
if (y > 0) {
// 第一页,下拉加载更多
if (this.page == 0){
if (!this.isRefresh){
this.setState({
refreshing:true,
isLoading:true
})
this.isRefresh = true
// 请求接口
this.onRefresh()
}
this.flatList.scrollToOffset({animated: false,offset: -y})
}else {
this.flatList.scrollToOffset({animated: false,offset: this.page*height-y})
}
} else{
y = Math.abs(y)
if (this.page == this.state.list.length - 1){
// 最后一页,上拉刷新
if (!this.isLoadMore){
this.setState({
isLoading:true,
})
this.isLoadMore = true
this.onEndReached()
}
}
this.flatList.scrollToOffset({animated: false, offset: this.page * height + y})
}
}
}
},
onPanResponderRelease: (evt, gestureState) => {
this.endTime = evt.nativeEvent.timestamp
let y = gestureState.dy
let x = gestureState.dx
if (Math.abs(x) > Math.abs(y)){
// 纵向重置状态
this.flatList.scrollToIndex({ animated: true, index: this.page });
// 横向滑动
if (x > 0){
if (this.state.cityListLeft == width){
}else {
if (x > width * this.minSlideScaleX){
this.setState({
cityListLeft:width
})
}else {
this.setState({
cityListLeft:0
})
}
LayoutAnimation.easeInEaseOut();
}
}else {
if (this.state.cityListLeft == 0){
}else {
if (Math.abs(x) > width * this.minSlideScaleX){
this.setState({
cityListLeft:0
})
}else {
this.setState({
cityListLeft:width
})
}
LayoutAnimation.easeInEaseOut();
}
}
}else {
// 纵向滑动
// 横向重置状态
this.setState({
cityListLeft:width
})
if (y > 0) {
// 向上
if (y > height * this.minSlideScaleY || (this.endTime - this.startTime < this.maxSlideTime && y > this.minSlideDistance)) {
if(this.page != 0){
this.page -= 1
}
}
this.flatList.scrollToIndex({ animated: true, index: this.page });
} else if (y < 0) {
// 向下
y = Math.abs(y)
if (y > height * this.minSlideScaleY || (this.endTime - this.startTime < this.maxSlideTime && y > this.minSlideDistance)) {
if(this.state.list.length != this.page + 1){
this.page += 1
}
}
this.flatList.scrollToIndex({ animated: true, index: this.page });
}
}
},
onPanResponderTerminate: (evt, gestureState) => {
},
onShouldBlockNativeResponder: (evt, gestureState) => {
return false;
},
});
this.cityPanResponder = PanResponder.create({
// 要求成为响应者:
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: (evt, gestureState) => {
},
onPanResponderMove: (evt, gestureState) => {
let x = gestureState.dx
// 横向滑动
if (Math.abs(x) > 30){
if (x > 0){
// 向右
this.setState({
cityListLeft:x
})
}
}
},
onPanResponderRelease: (evt, gestureState) => {
this.endTime = evt.nativeEvent.timestamp
let x = gestureState.dx
// 横向滑动
if (x > 0){
console.log('向右');
if (x > width * this.minSlideScaleX){
this.setState({
cityListLeft:width
})
}else {
this.setState({
cityListLeft:0
})
}
LayoutAnimation.easeInEaseOut();
}
},
onPanResponderTerminate: (evt, gestureState) => {
},
onShouldBlockNativeResponder: (evt, gestureState) => {
return false;
},
});
}
componentWillUnmount() {
}
componentDidMount(){
}
onEndReached=()=>{
console.log('onEndReached');
setTimeout(() => {
this.setState({
isLoading:false,
})
this.isLoadMore = false
}, 1500);
}
onRefresh=()=>{
// 请求接口数据,刷新
setTimeout(() => {
this.setState({
refreshing:false,
isLoading:false
})
this.isRefresh = false
}, 1500);
}
// 不要在render中定义
onViewableItemsChanged=(info)=>{
console.log('infoinfoinfoinfo===',info);
if(info.viewableItems.length > 0){
// 显示了新的item
}
}
render() {
return Render.render.call(this);
}
}
const mapStateToProps = state => {
return {
userInfo: state.userInfo,
globalInfo: state.globalInfo,
}
};
const mapDispatchToProps = dispatch => ({
getUserInfo: userInfo => dispatch({type: ActionTypes.GET_USERINFO, payload: {userInfo}}),
loginSuccess: globalInfo => dispatch({type: ActionTypes.LOGIN_SUCCESS, payload: {globalInfo}}),
setWalletMine: wallet => dispatch({type: ActionTypes.SET_WALLET_MINE, payload: wallet}),
});
export default connect(mapStateToProps, mapDispatchToProps)(FishpondList);
render.js
<View style={styles.mainView} {...this.panResponder.panHandlers}>
<FlatList ref={ref=>this.flatList = ref}
data={this.state.list}
renderItem={({ item, index }) => {
return <View key={index}>
<View style={[styles.item,{
backgroundColor:`rgb(${Math.random() * 255},${Math.random() * 255},${Math.random() * 255})`
}]}>
<Text style={{color:'#fff',fontSize:70}}>{index+1}</Text>
</View>
</View>
}}
ListHeaderComponent={this.state.refreshing ?<ListHeaderComponent/> : <View/>}
getItemLayout={(data, index) => (
{ length: styles.item.height, offset: styles.item.height * index, index }
)}
scrollEnabled={false}
onViewableItemsChanged={this.onViewableItemsChanged}
viewabilityConfig={this.viewabilityConfig}
showsVerticalScrollIndicator={false}
/>
</View>
<RightView panResponder={this.cityPanResponder} left={this.state.cityListLeft}/>