UNPKG

@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
'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) } }