diff --git a/README.md b/README.md
index 58a4840..9259064 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,24 @@
-### Custom Android Pull to Refresh
+### Custom Pull to Refresh Component
Inspired by the shots from the author: https://dribbble.com/yupnguyen
[Expo Demo](https://expo.io/@devilsanek/animated-pull-to-refresh)
-| Coffee Concept | Coin Concept | Weather Concept
-| ------------------------- |:-----------------------:|:-----------------------:|
-| | ||
+| Coffee Concept | Coin Concept | Weather Concept |
+| ----------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------: | :----------------------------------------------------------------------------------------------------------------------------------------: |
+|  |  |  |
### Description
-Currently, react-native provides RefreshControl out of the box. (Which uses standard circle android animation).
-https://facebook.github.io/react-native/docs/refreshcontrol.html.
+Currently, react-native provides RefreshControl out of the box. (Which uses standard circle android animation).
+https://facebook.github.io/react-native/docs/refreshcontrol.html.
-However, it is not 'yet' possible to override the animation that runs during refreshing phase. This package aims to fill this gap and provide a 'relatively' easy way to add your own custom animation.
+However, it is not 'yet' possible to override the animation that runs during refreshing phase. This package aims to fill this gap and provide a 'relatively' easy way to add your own custom animation.
### Installation
-1. Install the package using either:
+1. Install the package using either:
+
```sh
$ npm install --save react-native-pull-refresh
# or
@@ -25,7 +26,7 @@ $ yarn add react-native-pull-refresh
```
2. Install and link the Lottie package (renders Adobe After Effect animations):
-https://github.com/airbnb/lottie-react-native
+ https://github.com/airbnb/lottie-react-native
```sh
yarn add lottie-react-native
@@ -45,59 +46,58 @@ You can find `< Header />` and `< ScrollItem />` components in the sample folder
import PullToRefresh from 'react-native-pull-refresh';
export default class weatherAnimation extends Component {
- constructor( ) {
- super( );
- this.state = {
- isRefreshing: false,
- };
- }
-
- onRefresh() {
- this.setState({isRefreshing: true});
-
- // Simulate fetching data from the server
- setTimeout(() => {
- this.setState({isRefreshing: false});
- }, 5000);
- }
-
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
-
- onPullAnimationSrc ={require('./umbrella_pull.json')}
- onStartRefreshAnimationSrc ={require('./umbrella_start.json')}
- onRefreshAnimationSrc = {require('./umbrella_repeat.json')}
- onEndRefreshAnimationSrc = {require('./umbrella_end.json')}
- />
-
-
- );
- }
+ constructor() {
+ super();
+ this.state = {
+ isRefreshing: false,
+ };
+ }
+
+ onRefresh() {
+ this.setState({ isRefreshing: true });
+
+ // Simulate fetching data from the server
+ setTimeout(() => {
+ this.setState({ isRefreshing: false });
+ }, 5000);
+ }
+
+ render() {
+ return (
+
+
+
+
+ // content to pull goes here
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
}
```
#### Animation Files Format
+
Lottie JSON - https://github.com/airbnb/lottie-react-native
Lottie is a mobile library, developed by AirBnB for Android and iOS that parses Adobe After Effects animations exported as JSON with bodymovin and renders them natively on mobile.
@@ -108,36 +108,35 @@ You can find file examples in `examples/SimpleAnimations/animations` folder
#### General Props
-| Prop | Type | Description |
-|---|---|---|
-|**`isRefreshing`**|`Boolean`|Refresh state set by parent to trigger refresh.|
-|**`pullHeight`**|`Integer`|Pull Distance _Default 180._|
-|**`onRefresh`**|`Function`|Callback after refresh event|
-|**`contentView`**|`Object`|The content: ScrollView or ListView|
-|**`animationBackgroundColor`**|`string`|Background color|
-|**`onScroll`**|`Function`|Custom onScroll event|
+| Prop | Type | Description |
+| ------------------------------ | ---------- | ----------------------------------------------- |
+| **`isRefreshing`** | `Boolean` | Refresh state set by parent to trigger refresh. |
+| **`pullHeight`** | `Integer` | Pull Distance _Default 180._ |
+| **`onRefresh`** | `Function` | Callback after refresh event |
+| **`animationBackgroundColor`** | `string` | Background color |
+| **`onScroll`** | `Function` | Custom onScroll event |
#### Animation Source Files Props
-| Prop | Description |
-|---|---|
-|**`onPullAnimationSrc`**|Animation JSON that runs when scroll view is pulled down|
-|**`onStartRefreshAnimationSrc`**|Animation JSON that runs after view was pulled and released|
-|**`onRefreshAnimationSrc`**|Animation JSON that runs continuously until isRefreshing props is not changed|
-|**`onEndRefreshAnimationSrc`**|Animation JSON that runs after isRefreshing props is changed|
+| Prop | Description |
+| -------------------------------- | ----------------------------------------------------------------------------- |
+| **`onPullAnimationSrc`** | Animation JSON that runs when scroll view is pulled down |
+| **`onStartRefreshAnimationSrc`** | Animation JSON that runs after view was pulled and released |
+| **`onRefreshAnimationSrc`** | Animation JSON that runs continuously until isRefreshing props is not changed |
+| **`onEndRefreshAnimationSrc`** | Animation JSON that runs after isRefreshing props is changed |
### Demo
+
The demo app can be found at `examples/SimpleAnimations`.
Install Expo App on your [Android smartphone](https://play.google.com/store/apps/details?id=host.exp.exponent&referrer=www)
-Scan this QR-code with your Expo App.
+Scan this QR-code with your Expo App.

... or go [here](https://expo.io/@devilsanek/animated-pull-to-refresh) and try it out!
-
### Contribution / Issues
Are very welcome! :)
diff --git a/examples/SimpleAnimations/weatherAnimation/index.android.js b/examples/SimpleAnimations/weatherAnimation/index.android.js
index 0182b54..a6a5728 100644
--- a/examples/SimpleAnimations/weatherAnimation/index.android.js
+++ b/examples/SimpleAnimations/weatherAnimation/index.android.js
@@ -1,20 +1,5 @@
import React, { Component } from 'react';
-import {
- AppRegistry,
- Dimensions,
- PanResponder,
- View,
- Animated,
- ListView,
- RefreshControl,
- Text,
- Progress,
- StyleSheet,
- ScrollView,
- UIManager,
- StatusBar
-} from 'react-native';
-
+import { AppRegistry, Dimensions, View, ScrollView } from 'react-native';
import PullToRefresh from 'react-native-pull-refresh';
@@ -22,168 +7,162 @@ const HEIGHT = Dimensions.get('window').height;
const WIDTH = Dimensions.get('window').width;
class Header extends Component {
- constructor(props){
- super(props)
- this.state = {
- width: 0,
- height: 0
- }
- this.measureView = this.measureView.bind(this);
- }
-
- measureView(event) {
- this.setState({
- width: event.nativeEvent.layout.width,
- height: event.nativeEvent.layout.height
- })
- }
-
- render(){
- const mainStyle = {
- flex: 1,
- backgroundColor: '#F8F4FC',
- justifyContent: 'center',
- alignItems: 'center',
- borderBottomWidth: 1,
- borderBottomColor: '#8B8393'
- }
-
- const submenuStyle = {
- width: this.state.width / 2,
- height: this.state.height / 4,
- borderRadius: 50,
- backgroundColor: '#8B8393'
- }
- return (
- this.measureView(event)}>
-
-
- )
- }
+ constructor(props) {
+ super(props);
+ this.state = {
+ width: 0,
+ height: 0,
+ };
+ this.measureView = this.measureView.bind(this);
+ }
+
+ measureView(event) {
+ this.setState({
+ width: event.nativeEvent.layout.width,
+ height: event.nativeEvent.layout.height,
+ });
+ }
+
+ render() {
+ const mainStyle = {
+ flex: 1,
+ backgroundColor: '#F8F4FC',
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderBottomWidth: 1,
+ borderBottomColor: '#8B8393',
+ };
+
+ const submenuStyle = {
+ width: this.state.width / 2,
+ height: this.state.height / 4,
+ borderRadius: 50,
+ backgroundColor: '#8B8393',
+ };
+ return (
+ this.measureView(event)}>
+
+
+ );
+ }
}
class ScrollItem extends Component {
- constructor(props){
- super(props)
- this.state = {
- height: 100
- }
- }
-
- render(){
- const mainStyle = {
- flex: 1,
- height: this.state.height,
- backgroundColor: '#DCDADF',
- flexDirection: 'row',
- justifyContent: 'center',
- alignItems: 'center',
- borderBottomWidth: 1,
- borderBottomColor: '#8B8393'
- }
- const imgContainer = {
- flex: 1,
- justifyContent: 'center',
- alignItems: 'center'
- }
-
- const imgStyle = {
- width: this.state.height / 1.5,
- height: this.state.height / 1.5,
- backgroundColor: '#ADA8B3',
- borderRadius: 10
- }
-
- const textContainer = {
- flex: 3,
- height: this.state.height / 1.5,
- flexDirection: 'column',
- justifyContent: 'flex-start'
- }
-
- const textStyle = {
- width: WIDTH / 1.8,
- marginBottom: 10,
- height: this.state.height / 8,
- backgroundColor: '#ADA8B3',
- borderRadius: 10
- }
-
- const textStyleShort = {
- width: WIDTH / 3,
- marginBottom: 10,
- height: this.state.height / 9,
- backgroundColor: '#ADA8B3',
- borderRadius: 12
- }
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
- )
- }
+ constructor(props) {
+ super(props);
+ this.state = {
+ height: 100,
+ };
+ }
+
+ render() {
+ const mainStyle = {
+ flex: 1,
+ height: this.state.height,
+ backgroundColor: '#DCDADF',
+ flexDirection: 'row',
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderBottomWidth: 1,
+ borderBottomColor: '#8B8393',
+ };
+ const imgContainer = {
+ flex: 1,
+ justifyContent: 'center',
+ alignItems: 'center',
+ };
+
+ const imgStyle = {
+ width: this.state.height / 1.5,
+ height: this.state.height / 1.5,
+ backgroundColor: '#ADA8B3',
+ borderRadius: 10,
+ };
+
+ const textContainer = {
+ flex: 3,
+ height: this.state.height / 1.5,
+ flexDirection: 'column',
+ justifyContent: 'flex-start',
+ };
+
+ const textStyle = {
+ width: WIDTH / 1.8,
+ marginBottom: 10,
+ height: this.state.height / 8,
+ backgroundColor: '#ADA8B3',
+ borderRadius: 10,
+ };
+
+ const textStyleShort = {
+ width: WIDTH / 3,
+ marginBottom: 10,
+ height: this.state.height / 9,
+ backgroundColor: '#ADA8B3',
+ borderRadius: 12,
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
}
-
export default class weatherAnimation extends Component {
- constructor(props) {
- super(props);
- this.state = {
- isRefreshing: false,
- };
- }
-
- onRefresh() {
- this.setState({isRefreshing: true});
- setTimeout(() => {
- this.setState({isRefreshing: false});
- }, 5000);
- }
-
- render() {
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
-
- onPullAnimationSrc ={require('./umbrella_1.json')}
- onStartRefreshAnimationSrc ={require('./umbrella_start.json')}
- onRefreshAnimationSrc = {require('./umbrella_repeat.json')}
- onEndRefreshAnimationSrc = {require('./umbrella_end.json')}
- />
-
-
- );
- }
+ constructor(props) {
+ super(props);
+ this.state = {
+ isRefreshing: false,
+ };
+ }
+
+ onRefresh() {
+ this.setState({ isRefreshing: true });
+ setTimeout(() => {
+ this.setState({ isRefreshing: false });
+ }, 5000);
+ }
+
+ render() {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
}
-
AppRegistry.registerComponent('weatherAnimation', () => weatherAnimation);
diff --git a/src/AnimatedPullToRefresh.js b/src/AnimatedPullToRefresh.js
index 7f364c8..2bb9534 100644
--- a/src/AnimatedPullToRefresh.js
+++ b/src/AnimatedPullToRefresh.js
@@ -1,296 +1,239 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import {
- View,
- ScrollView,
- Animated,
- PanResponder,
- UIManager,
- Dimensions,
-} from 'react-native';
+import React, { useState, useEffect, useRef } from 'react';
+import { View, ScrollView, Animated, PanResponder, UIManager, Dimensions } from 'react-native';
import Animation from 'lottie-react-native';
-class AnimatedPullToRefresh extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- scrollY: new Animated.Value(0),
- refreshHeight: new Animated.Value(0),
- currentY: 0,
- isScrollFree: false,
-
- isRefreshAnimationStarted: false,
- isRefreshAnimationEnded: false,
- initAnimationProgress: new Animated.Value(0),
- repeatAnimationProgress: new Animated.Value(0),
- finalAnimationProgress: new Animated.Value(0),
- };
-
- this.onRepeatAnimation = this.onRepeatAnimation.bind(this);
- this.onEndAnimation = this.onEndAnimation.bind(this);
-
- UIManager.setLayoutAnimationEnabledExperimental &&
- UIManager.setLayoutAnimationEnabledExperimental(true);
- }
-
- static propTypes = {
- /**
- * Refresh state set by parent to trigger refresh
- * @type {Boolean}
- */
- isRefreshing: PropTypes?.bool.isRequired,
- /**
- * Pull Distance
- * @type {Integer}
- */
- pullHeight: PropTypes?.number,
- /**
- * Callback after refresh event
- * @type {Function}
- */
- onRefresh: PropTypes?.func.isRequired,
- /**
- * The content: ScrollView or ListView
- * @type {Object}
- */
- contentView: PropTypes?.object.isRequired,
- /**
- * Background color
- * @type {string}
- */
- animationBackgroundColor: PropTypes?.string,
- /**
- * Custom onScroll event
- * @type {Function}
- */
- onScroll: PropTypes.func,
- };
-
- static defaultProps = {
- pullHeight: 180,
- animationBackgroundColor: 'white',
- };
-
- componentWillMount() {
- this._panResponder = PanResponder.create({
- onStartShouldSetPanResponder: this._handleStartShouldSetPanResponder.bind(
- this,
- ),
- onMoveShouldSetPanResponder: this._handleMoveShouldSetPanResponder.bind(
- this,
- ),
- onPanResponderMove: this._handlePanResponderMove.bind(this),
- onPanResponderRelease: this._handlePanResponderEnd.bind(this),
- onPanResponderTerminate: this._handlePanResponderEnd.bind(this),
- });
- }
-
- componentWillReceiveProps(props) {
- if (this.props.isRefreshing !== props.isRefreshing) {
- // Finish the animation and set refresh panel height to 0
- if (!props.isRefreshing) {
- }
- }
- }
-
- _handleStartShouldSetPanResponder(e, gestureState) {
- return !this.state.isScrollFree;
- }
-
- _handleMoveShouldSetPanResponder(e, gestureState) {
- return !this.state.isScrollFree;
- }
-
- // if the content scroll value is at 0, we allow for a pull to refresh
- _handlePanResponderMove(e, gestureState) {
- if (!this.props.isRefreshing) {
- if (
- (gestureState.dy >= 0 && this.state.scrollY._value === 0) ||
- this.state.refreshHeight._value > 0
- ) {
- this.state.refreshHeight.setValue(-1 * gestureState.dy * 0.5);
- } else {
- // Native android scrolling
- this.refs.scrollComponentRef.scrollTo({
- y: -1 * gestureState.dy,
- animated: true,
- });
- }
- }
- }
-
- _handlePanResponderEnd(e, gestureState) {
- if (!this.props.isRefreshing) {
- if (this.state.refreshHeight._value <= -this.props.pullHeight) {
- this.onScrollRelease();
- Animated.parallel([
- Animated.spring(this.state.refreshHeight, {
- toValue: -this.props.pullHeight,
- }),
- Animated.timing(this.state.initAnimationProgress, {
- toValue: 1,
- duration: 1000,
- }),
- ]).start(() => {
- this.state.initAnimationProgress.setValue(0);
- this.setState({isRefreshAnimationStarted: true});
- this.onRepeatAnimation();
- });
- } else if (this.state.refreshHeight._value <= 0) {
- Animated.spring(this.state.refreshHeight, {
- toValue: 0,
- }).start();
- }
-
- if (this.state.scrollY._value > 0) {
- this.setState({isScrollFree: true});
- }
- }
- }
-
- onRepeatAnimation() {
- this.state.repeatAnimationProgress.setValue(0);
-
- Animated.timing(this.state.repeatAnimationProgress, {
- toValue: 1,
- duration: 1000,
- }).start(() => {
- if (this.props.isRefreshing) {
- this.onRepeatAnimation();
- } else {
- this.state.repeatAnimationProgress.setValue(0);
- this.onEndAnimation();
- }
- });
- }
-
- onEndAnimation() {
- this.setState({isRefreshAnimationEnded: true});
- Animated.sequence([
- Animated.timing(this.state.finalAnimationProgress, {
- toValue: 1,
- duration: 1000,
- }),
- Animated.spring(this.state.refreshHeight, {
- toValue: 0,
- bounciness: 12,
- }),
- ]).start(() => {
- this.state.finalAnimationProgress.setValue(0);
- this.setState({
- isRefreshAnimationEnded: false,
- isRefreshAnimationStarted: false,
- });
- });
- }
-
-
- onScrollRelease() {
- if (!this.props.isRefreshing) {
- this.props.onRefresh();
- }
- }
-
- isScrolledToTop() {
- if (this.state.scrollY._value === 0 && this.state.isScrollFree) {
- this.setState({isScrollFree: false});
- }
- }
-
- render() {
- const onScrollEvent = event => {
- this.state.scrollY.setValue(event.nativeEvent.contentOffset.y);
- };
-
- const animateHeight = this.state.refreshHeight.interpolate({
- inputRange: [-this.props.pullHeight, 0],
- outputRange: [this.props.pullHeight, 0],
- });
-
- const animateProgress = this.state.refreshHeight.interpolate({
- inputRange: [-this.props.pullHeight, 0],
- outputRange: [1, 0],
- extrapolate: 'clamp',
- });
-
- const animationStyle = {
- position: 'absolute',
- top: 0,
- bottom: 0,
- right: 0,
- left: 0,
- backgroundColor: this.props.animationBackgroundColor,
- width: Dimensions.get('window').width,
- height: this.props.pullHeight,
- };
-
- return (
-
-
-
-
-
-
- {
- this.isScrolledToTop();
- }}
- onScrollEndDrag={() => {
- this.isScrolledToTop();
- }}>
-
- {React.cloneElement(this.props.contentView, {
- scrollEnabled: false,
- ref: 'scrollComponentRef',
- })}
-
-
-
- );
- }
-}
-
-module.exports = AnimatedPullToRefresh;
+const AnimatedPullToRefresh = ({
+ isRefreshing,
+ onRefresh,
+ pullHeight = 180,
+ animationBackgroundColor = 'white',
+ onPullAnimationSrc,
+ onStartRefreshAnimationSrc,
+ onRefreshAnimationSrc,
+ onEndRefreshAnimationSrc,
+ children,
+}) => {
+ const [_state, _setState] = useState({
+ currentY: 0,
+ isScrollFree: false,
+ isRefreshAnimationStarted: false,
+ isRefreshAnimationEnded: false,
+ });
+ const [_animationCount, _setAnimationCount] = useState(0);
+ const scrollY = useRef(new Animated.Value(0)).current;
+ const refreshHeight = useRef(new Animated.Value(0)).current;
+ const initAnimationProgress = useRef(new Animated.Value(0)).current;
+ const repeatAnimationProgress = useRef(new Animated.Value(0)).current;
+ const finalAnimationProgress = useRef(new Animated.Value(0)).current;
+
+ const scrollComponentRef = useRef(null);
+ const _panResponder = useRef(null);
+
+ useEffect(() => {
+ _panResponder.current = PanResponder.create({
+ onStartShouldSetPanResponder: _handleStartShouldSetPanResponder,
+ onMoveShouldSetPanResponder: _handleMoveShouldSetPanResponder,
+ onPanResponderMove: _handlePanResponderMove,
+ onPanResponderRelease: _handlePanResponderEnd,
+ onPanResponderTerminate: _handlePanResponderEnd,
+ });
+ UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
+ }, []);
+
+ const _handleStartShouldSetPanResponder = (e, gestureState) => {
+ return !_state.isScrollFree;
+ };
+
+ const _handleMoveShouldSetPanResponder = (e, gestureState) => {
+ return !_state.isScrollFree;
+ };
+
+ //if the content scroll value is at 0, we allow for a pull to refresh
+ const _handlePanResponderMove = (e, gestureState) => {
+ if (!isRefreshing) {
+ if ((gestureState.dy >= 0 && scrollY?._value === 0) || refreshHeight?._value > 0) {
+ refreshHeight?.setValue(-1 * gestureState.dy * 0.5);
+ } else {
+ // Native android scrolling
+ scrollComponentRef?.current?.scrollTo({
+ y: -1 * gestureState.dy,
+ animated: true,
+ });
+ }
+ }
+ };
+
+ const _handlePanResponderEnd = (e, gestureState) => {
+ if (!isRefreshing) {
+ if (refreshHeight?._value <= -pullHeight) {
+ onScrollRelease();
+ Animated.parallel([
+ Animated.spring(refreshHeight, {
+ toValue: -pullHeight,
+ useNativeDriver: false,
+ }),
+ Animated.timing(initAnimationProgress, {
+ useNativeDriver: false,
+ toValue: 1,
+ duration: 1000,
+ }),
+ ]).start(() => {
+ initAnimationProgress?.setValue(0);
+ _setState({
+ ..._state,
+ isRefreshAnimationStarted: true,
+ isRefreshAnimationEnded: false,
+ });
+ onRepeatAnimation();
+ });
+ } else if (refreshHeight?._value <= 0) {
+ Animated.spring(refreshHeight, {
+ toValue: 0,
+ useNativeDriver: false,
+ }).start();
+ }
+
+ if (scrollY?._value > 0) {
+ _setState({ ..._state, isScrollFree: true });
+ }
+ }
+ };
+ useEffect(() => {
+ if (!_state.isRefreshAnimationStarted && isRefreshing) {
+ refreshHeight.setValue(-pullHeight);
+ repeatAnimationProgress?.setValue(1);
+ _setState({
+ ..._state,
+ isRefreshAnimationStarted: true,
+ isRefreshAnimationEnded: false,
+ });
+ onRepeatAnimation();
+ }
+ }, [_state.isRefreshAnimationStarted, isRefreshing]);
+ useEffect(() => onRepeatAnimation(), [_animationCount]);
+
+ const onRepeatAnimation = () => {
+ repeatAnimationProgress?.setValue(0);
+ Animated.timing(repeatAnimationProgress, {
+ useNativeDriver: false,
+ toValue: 1,
+ duration: 1000,
+ }).start(() => {
+ if (isRefreshing) {
+ _setAnimationCount((cc) => cc + 1);
+ } else {
+ repeatAnimationProgress?.setValue(0);
+ onEndAnimation();
+ }
+ });
+ };
+
+ const onEndAnimation = () => {
+ _setState({ ..._state, isRefreshAnimationEnded: true });
+ Animated.sequence([
+ Animated.timing(finalAnimationProgress, {
+ useNativeDriver: false,
+ toValue: 1,
+ duration: 1000,
+ }),
+ Animated.spring(refreshHeight, {
+ useNativeDriver: false,
+ toValue: 0,
+ bounciness: 12,
+ }),
+ ]).start(() => {
+ finalAnimationProgress?.setValue(0);
+ _setState({
+ ..._state,
+ isRefreshAnimationEnded: false,
+ isRefreshAnimationStarted: false,
+ });
+ });
+ };
+
+ const onScrollRelease = () => {
+ if (!isRefreshing) {
+ onRefresh();
+ }
+ };
+
+ const isScrolledToTop = () => {
+ if (scrollY?._value === 0 && _state.isScrollFree) {
+ _setState({ ..._state, isScrollFree: false });
+ }
+ };
+ let onScrollEvent = (event) => {
+ scrollY?.setValue(event.nativeEvent.contentOffset.y);
+ };
+
+ let animateHeight = refreshHeight?.interpolate({
+ inputRange: [-pullHeight, 0],
+ outputRange: [pullHeight, 0],
+ });
+
+ let animateProgress = refreshHeight?.interpolate({
+ inputRange: [-pullHeight, 0],
+ outputRange: [1, 0],
+ extrapolate: 'clamp',
+ });
+
+ const animationStyle = {
+ position: 'absolute',
+ top: 0,
+ bottom: 0,
+ right: 0,
+ left: 0,
+ backgroundColor: animationBackgroundColor,
+ width: Dimensions.get('window').width,
+ height: pullHeight,
+ };
+
+ return (
+
+
+
+
+
+
+ {
+ isScrolledToTop();
+ }}
+ onScrollEndDrag={() => {
+ isScrolledToTop();
+ }}>
+
+ {React.cloneElement(children, {
+ scrollEnabled: false,
+ ref: scrollComponentRef,
+ })}
+
+
+
+ );
+};
+
+export default AnimatedPullToRefresh;