@cqfw/react-native-pull-to-refresh
Version:
a pull to refresh component for react-native, same api on both android and ios.
175 lines (162 loc) • 7.29 kB
JavaScript
'use strict';
import React from 'react';
import {ListView, View, PanResponder, Animated, Easing, FlatList} from 'react-native';
import * as index from './info';
import PullRoot from './PullRoot'
export default class Pullable extends PullRoot {
constructor(props) {
super(props);
this.pullState = 'pulling'; //pulling,pullok,pullrelease
this.topIndicatorHeight = this.props.topIndicatorHeight ? this.props.topIndicatorHeight : index.defaultTopIndicatorHeight;
this.defaultXY = {x: 0, y: this.topIndicatorHeight * -1};
this.duration = this.props.duration ? this.props.duration : index.defaultDuration;
this.state = Object.assign({}, props, {
pullPan: new Animated.ValueXY(this.defaultXY),
atTop: true,
height: 0,
width: 0
});
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: this.onShouldSetPanResponder,
onStartShouldSetPanResponderCapture: this.onShouldSetPanResponder,
onMoveShouldSetPanResponder: this.onShouldSetPanResponder,
onMoveShouldSetPanResponderCapture: this.onShouldSetPanResponder,
onPanResponderTerminationRequest: (evt, gestureState) => false, //这个很重要,这边不放权
onPanResponderMove: this.onPanResponderMove,
onPanResponderRelease: this.onPanResponderRelease,
onPanResponderTerminate: this.onPanResponderRelease,
});
}
onShouldSetPanResponder = (e, gesture) => {
let y = 0
if (this.scroll instanceof ListView) { //ListView下的判断
y = this.scroll.scrollProperties.offset;
} else if (this.scroll instanceof FlatList) {//FlatList下的判断
y = this.scroll._listRef._getScrollMetrics().offset
}
//根据y的值来判断是否到达顶部
this.state.atTop = (y <= 0)
if (this.state.atTop && index.isDownGesture(gesture.dx, gesture.dy) && this.props.refreshable) {
this.lastY = this.state.pullPan.y._value;
return true;
}
return false;
}
onPanResponderMove = (e, gesture) => {
if (index.isDownGesture(gesture.dx, gesture.dy) && this.props.refreshable) { //下拉
this.state.pullPan.setValue({x: this.defaultXY.x, y: this.lastY + gesture.dy / 2});
this.onPullStateChange(gesture.dy)
}
}
onPanResponderRelease = (e, gesture) => {
if (this.pullState == 'pulling') { //没有下拉到位
this.resetDefaultXYHandler(); //重置状态
} else if (this.pullState == 'pullok') { //已经下拉到位了
//传入-1,表示此时进行的是释放刷新的操作
this.onPullStateChange(-1)
//进行下拉刷新的回调
this.props.onPullRelease && this.props.onPullRelease();
//重置刷新的头部到初始位置
Animated.timing(this.state.pullPan, {
toValue: {x: 0, y: 0},
easing: Easing.linear,
duration: this.duration
}).start();
}
}
//重置刷新的操作
resetDefaultXYHandler = () => {
Animated.timing(this.state.pullPan, {
toValue: this.defaultXY,
easing: Easing.linear,
duration: this.duration
}).start(() => {
//ui要进行刷新
this.onPullStateChange(-1)
});
}
/** 数据加载完成后调用此方法进行重置归位
*/
finishRefresh = () => {
if (this.pullState == 'pullrelease') { //仅触摸松开时才触发
this.resetDefaultXYHandler();
}
}
startRefresh = () => {
if (!this.props.refreshable) { //不支持下拉刷新的时候就不进行了
return;
}
//进行数据的回调
this.props.onPullRelease && this.props.onPullRelease();
//此时进行状态的改变
this.onPullStateChange(-1)
//动画的展示
Animated.timing(this.state.pullPan, {
toValue: {x: 0, y: 0},
easing: Easing.linear,
duration: this.duration
}).start();
}
onLayout = (e) => {
if (this.state.width != e.nativeEvent.layout.width || this.state.height != e.nativeEvent.layout.height) {
this.scrollContainer && this.scrollContainer.setNativeProps({
style: {
width: e.nativeEvent.layout.width,
height: e.nativeEvent.layout.height
}
});
this.state.width = e.nativeEvent.layout.width;
this.state.height = e.nativeEvent.layout.height;
}
}
render() {
return (
<View style={{flex: 1, flexGrow: 1, zIndex: -999}} {...this.panResponder.panHandlers} onLayout={this.onLayout}>
{this.props.isContentScroll ?
<View pointerEvents='box-none'>
<Animated.View style={[this.state.pullPan.getLayout()]}>
{this.renderTopIndicator()}
<View ref={(c) => {
this.scrollContainer = c;
}}
style={{width: this.state.width, height: this.state.height}}>
{this.getScrollable()}
</View>
</Animated.View>
</View> :
<View>
<View ref={(c) => {
this.scrollContainer = c;
}}
style={{width: this.state.width, height: this.state.height}}>
{this.getScrollable()}
</View>
<View pointerEvents='box-none'
style={{position: 'absolute', left: 0, right: 0, top: 0}}>
<Animated.View style={[this.state.pullPan.getLayout()]}>
{this.renderTopIndicator()}
</Animated.View>
</View>
</View>}
</View>
);
}
//下拉的时候根据高度进行对应的操作
onPullStateChange = (moveHeight) => {
//因为返回的moveHeight单位是px,所以要将this.topIndicatorHeight转化为px进行计算
let topHeight = index.dip2px(this.topIndicatorHeight)
if (moveHeight > 0 && moveHeight < topHeight) { //此时是下拉没有到位的状态
this.pullState = "pulling"
} else if (moveHeight >= topHeight) { //下拉刷新到位
this.pullState = "pullok"
} else { //下拉刷新释放,此时返回的值为-1
this.pullState = "pullrelease"
}
//默认的设置
this.defaultTopSetting()
//告诉外界是否要锁住
this.props.onPushing && this.props.onPushing(this.pullState != "pullrelease")
//进行状态和下拉距离的回调
this.props.onPullStateChangeHeight && this.props.onPullStateChangeHeight(this.pullState, moveHeight)
}
}