Skip to content

Instantly share code, notes, and snippets.

@mitchell-garcia
Last active September 24, 2019 13:24
Show Gist options
  • Save mitchell-garcia/6ee4d5b5c79162e3cc13b67d743777e6 to your computer and use it in GitHub Desktop.
Save mitchell-garcia/6ee4d5b5c79162e3cc13b67d743777e6 to your computer and use it in GitHub Desktop.
import React from 'react'
import { Dimensions, Animated, View, Easing, Platform } from 'react-native'
import PropTypes from 'prop-types'
/**
* DismissableScrollview
* Will dismiss element when the user scrolls from the top and there is no more vertical
* scrolling available.
*/
export default class DismissableScrollview extends React.Component {
static propTypes = {
onSwipeDownThreshold: PropTypes.number,
onSwipeAway: PropTypes.func,
children: PropTypes.node
}
static defaultProps = {
onBeforeSwipeAway: () => { },
onSwipeAway: () => { },
// Account for lack of overscroll in Android
onSwipeDownThreshold: Platform.OS === 'android' ? 0 : -10
}
constructor() {
super()
this.state = {
cardLocation: new Animated.Value(Dimensions.get('screen').height),
isTransparentBackground: true
}
Animated.timing(this.state.cardLocation, {
duration: 250,
easing: Easing.inOut(Easing.quad),
toValue: 0,
delay: 0,
useNativeDriver: true
}).start()
this._startDragValue = 0
}
animateAway(onCompleteCb = () => { }) {
this.props.onBeforeSwipeAway()
Animated.timing(this.state.cardLocation, {
toValue: Dimensions.get('screen').height,
duration: 300,
delay: 0,
useNativeDriver: true
}).start(onCompleteCb)
}
_onScroll(e) {
if (e.nativeEvent.contentOffset.y > 5 && this.state.isTransparentBackground) {
this.setState({
isTransparentBackground: false
})
}
if (e.nativeEvent.contentOffset.y < 5 && !this.state.isTransparentBackground) {
this.setState({
isTransparentBackground: true
})
}
if (this.props.onScroll) {
this.props.onScroll(e)
}
}
_onScrollBeginDrag(e) {
this._startDragValue = e.nativeEvent.contentOffset.y
}
_onScrollEndDrag(e) {
if (Platform.OS === 'android' && this._startDragValue >= 5) return
if (e.nativeEvent.contentOffset.y <= this.props.onSwipeDownThreshold) {
this.animateAway(() => {
this.props.onSwipeAway()
})
}
}
render() {
return (
<Animated.ScrollView
scrollEventThrottle={16}
onScroll={this._onScroll.bind(this)}
onScrollBeginDrag={this._onScrollBeginDrag.bind(this)}
onScrollEndDrag={this._onScrollEndDrag.bind(this)}
contentContainerStyle={this.props.contentContainerStyle}
alwaysBounceVertical={true}
style={{
top: 0,
left: 0,
right: 0,
position: 'absolute',
height: Dimensions.get('window').height,
backgroundColor: this.state.isTransparentBackground ? 'transparent' : '#fff',
transform: [
{
translateY: this.state.cardLocation
}
]
}}
>
<View
style={{
minHeight: Dimensions.get('window').height,
shadowColor: this.state.isTransparentBackground ? '#333' : '#fff',
shadowOffset: {
height: 0
},
shadowOpacity: .3,
shadowRadius: 10,
backgroundColor: '#fff'
}}
>
{this.props.children}
</View>
</Animated.ScrollView>
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment