Created
March 17, 2017 09:27
-
-
Save satya164/74bd8bee4f61dec590ea5bc693013a15 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* @flow */ | |
import React, { PureComponent, PropTypes } from 'react'; | |
import { | |
Animated, | |
View, | |
StyleSheet, | |
ScrollView, | |
} from 'react-native'; | |
const AnimatedScrollView = Animated.createAnimatedComponent(ScrollView); | |
const styles = StyleSheet.create({ | |
constainer: { | |
flex: 1, | |
}, | |
header: { | |
position: 'absolute', | |
top: 0, | |
left: 0, | |
right: 0, | |
}, | |
}); | |
type Props = { | |
header: React.Element<*>; | |
} | |
type State = { | |
headerHeight: number, | |
scrollAnim: Animated.Value, | |
offsetAnim: Animated.Value, | |
} | |
export default class CollapsingHeaderScrollView extends PureComponent<void, Props, State> { | |
static propTypes = { | |
header: PropTypes.node.isRequired, | |
}; | |
state = { | |
headerHeight: 0, | |
scrollAnim: new Animated.Value(0), | |
offsetAnim: new Animated.Value(0), | |
}; | |
componentDidMount() { | |
this.state.scrollAnim.addListener(this._handleScroll); | |
} | |
componentWillUnmount() { | |
this.state.scrollAnim.removeListener(this._handleScroll); | |
} | |
_previousScrollvalue: number = 0; | |
_currentScrollValue: number = 0; | |
_scrollEndTimer: any; | |
_handleLayout = e => { | |
if (this.state.headerHeight === e.nativeEvent.layout.height) { | |
return; | |
} | |
this.setState({ | |
headerHeight: e.nativeEvent.layout.height, | |
}); | |
} | |
_handleScroll = ({ value }) => { | |
this._previousScrollvalue = this._currentScrollValue; | |
this._currentScrollValue = value; | |
}; | |
_handleScrollEndDrag = () => { | |
this._scrollEndTimer = setTimeout(this._handleMomentumScrollEnd, 250); | |
}; | |
_handleMomentumScrollBegin = () => { | |
clearTimeout(this._scrollEndTimer); | |
}; | |
_handleMomentumScrollEnd = () => { | |
const previous = this._previousScrollvalue; | |
const current = this._currentScrollValue; | |
if (previous > current || current < (this.state.headerHeight / 2)) { | |
// User scrolled down or scroll amount was too less, lets snap back our header | |
Animated.spring(this.state.offsetAnim, { | |
toValue: -current, | |
tension: 300, | |
friction: 35, | |
useNativeDriver: true, | |
}).start(); | |
} else { | |
Animated.timing(this.state.offsetAnim, { | |
toValue: 0, | |
duration: 500, | |
useNativeDriver: true, | |
}).start(); | |
} | |
}; | |
render() { | |
const { header, ...rest } = this.props; | |
const { scrollAnim, offsetAnim } = this.state; | |
const translateY = this.state.headerHeight ? Animated.add(scrollAnim, offsetAnim).interpolate({ | |
inputRange: [0, this.state.headerHeight], | |
outputRange: [0, -this.state.headerHeight], | |
extrapolate: 'clamp', | |
}) : 0; | |
return ( | |
<View style={styles.container}> | |
<AnimatedScrollView | |
{...rest} | |
scrollEventThrottle={16} | |
onScroll={Animated.event( | |
[ { nativeEvent: { contentOffset: { y: this.state.scrollAnim } } } ], | |
{ useNativeDriver: true } | |
)} | |
onMomentumScrollBegin={this._handleMomentumScrollBegin} | |
onMomentumScrollEnd={this._handleMomentumScrollEnd} | |
onScrollEndDrag={this._handleScrollEndDrag} | |
/> | |
<Animated.View onLayout={this._handleLayout} style={[styles.header, { transform: [{ translateY }] }]}> | |
{header} | |
</Animated.View> | |
</View> | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment