Created
February 26, 2019 08:01
-
-
Save somangshu/9cbd75b1dedf5c39fc00362eb8f5169a to your computer and use it in GitHub Desktop.
React native animated scrollview
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
import React, { Component } from 'react'; | |
import { | |
Animated, | |
Platform, | |
StatusBar, | |
StyleSheet, | |
Text, | |
View, | |
RefreshControl, | |
} from 'react-native'; | |
import MealDetailView from './MealDetailView'; | |
import { RkButton } from 'react-native-ui-kitten'; | |
import { SCREEN_WIDTH } from '../assets/theme'; | |
import axios from 'axios'; | |
import { Loading } from '../components/Loading'; | |
const HEADER_MAX_HEIGHT = 250; | |
const HEADER_MIN_HEIGHT = Platform.OS === 'ios' ? 60 : 73; | |
const HEADER_SCROLL_DISTANCE = HEADER_MAX_HEIGHT - HEADER_MIN_HEIGHT; | |
export default class App extends Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
scrollY: new Animated.Value( | |
// iOS has negative initial scroll value because content inset... | |
Platform.OS === 'ios' ? -HEADER_MAX_HEIGHT : 0, | |
), | |
refreshing: false, | |
details: {}, | |
isLoading: true | |
}; | |
} | |
componentDidMount() { | |
axios.get('/catalog/' + this.props.navigation.state.params.id).then((response) => { | |
this.setState({ | |
details: response.data, | |
isLoading: false | |
}) | |
}).catch((error) => { | |
console.log('geg', error) | |
}); | |
} | |
_renderScrollViewContent() { | |
return ( | |
<View style={styles.scrollViewContent}> | |
<MealDetailView screenProps={{ details: this.state.details, navigation: this.props.navigation }} /> | |
</View> | |
); | |
} | |
render() { | |
// Because of content inset the scroll value will be negative on iOS so bring | |
// it back to 0. | |
const scrollY = Animated.add( | |
this.state.scrollY, | |
Platform.OS === 'ios' ? HEADER_MAX_HEIGHT : 0, | |
); | |
const headerTranslate = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE], | |
outputRange: [0, -HEADER_SCROLL_DISTANCE], | |
extrapolate: 'clamp', | |
}); | |
const imageOpacity = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE / 2, HEADER_SCROLL_DISTANCE], | |
outputRange: [0.6, 0.5, 0.2], | |
extrapolate: 'clamp', | |
}); | |
const imageTranslate = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE], | |
outputRange: [0, 100], | |
extrapolate: 'clamp', | |
}); | |
const titleScale = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE / 2, HEADER_SCROLL_DISTANCE], | |
outputRange: [1, 1, 0.9], | |
extrapolate: 'clamp', | |
}); | |
const titleTranslate = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE / 2, HEADER_SCROLL_DISTANCE], | |
outputRange: [0, 0, -22], | |
extrapolate: 'clamp', | |
}); | |
if (this.state.isLoading) { | |
return <Loading size={26} /> | |
} else { | |
return ( | |
<View style={styles.fill}> | |
<StatusBar | |
translucent | |
barStyle="light-content" | |
backgroundColor="#fff" | |
/> | |
<Animated.ScrollView | |
style={styles.fill} | |
scrollEventThrottle={1} | |
onScroll={Animated.event( | |
[{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }], | |
{ useNativeDriver: true }, | |
)} | |
refreshControl={ | |
<RefreshControl | |
refreshing={this.state.refreshing} | |
onRefresh={() => { | |
this.setState({ refreshing: true }); | |
setTimeout(() => this.setState({ refreshing: false }), 1000); | |
}} | |
// Android offset for RefreshControl | |
progressViewOffset={HEADER_MAX_HEIGHT} | |
/> | |
} | |
// iOS offset for RefreshControl | |
contentInset={{ | |
top: HEADER_MAX_HEIGHT, | |
}} | |
contentOffset={{ | |
y: -HEADER_MAX_HEIGHT, | |
}} | |
> | |
{this._renderScrollViewContent()} | |
</Animated.ScrollView> | |
<Animated.View | |
pointerEvents="none" | |
style={[ | |
styles.header, | |
{ transform: [{ translateY: headerTranslate }] }, | |
]} | |
> | |
<Animated.Image | |
style={[ | |
styles.backgroundImage, | |
{ | |
opacity: imageOpacity, | |
transform: [{ translateY: imageTranslate }], | |
}, | |
]} | |
source={require('../images/rotee_small.png')} | |
/> | |
</Animated.View> | |
<Animated.View | |
style={[ | |
styles.bar, | |
{ | |
transform: [ | |
{ scale: titleScale }, | |
{ translateY: titleTranslate }, | |
], | |
}, | |
]} | |
> | |
<Text style={styles.title}>{this.state.details.dish_name}</Text> | |
</Animated.View> | |
<RkButton style={styles.addButton}>Add To Cart</RkButton> | |
</View> | |
); | |
} | |
} | |
} | |
const styles = StyleSheet.create({ | |
fill: { | |
flex: 1, | |
}, | |
content: { | |
flex: 1, | |
}, | |
header: { | |
position: 'absolute', | |
top: 0, | |
left: 0, | |
right: 0, | |
backgroundColor: '#000', | |
overflow: 'hidden', | |
height: HEADER_MAX_HEIGHT, | |
}, | |
backgroundImage: { | |
position: 'absolute', | |
top: 0, | |
left: 0, | |
right: 0, | |
width: null, | |
opacity: 0.2, | |
height: HEADER_MAX_HEIGHT, | |
resizeMode: 'cover', | |
}, | |
bar: { | |
backgroundColor: 'transparent', | |
marginTop: Platform.OS === 'ios' ? 28 : 38, | |
height: 32, | |
alignItems: 'center', | |
justifyContent: 'center', | |
position: 'absolute', | |
top: 0, | |
left: 0, | |
right: 0, | |
}, | |
title: { | |
color: 'white', | |
fontSize: 22, | |
}, | |
scrollViewContent: { | |
// iOS uses content inset, which acts like padding. | |
paddingTop: Platform.OS !== 'ios' ? HEADER_MAX_HEIGHT : 0, | |
}, | |
addButton: { | |
backgroundColor: '#a1448a', | |
position: 'absolute', | |
bottom: 0, | |
height: 40, | |
width: SCREEN_WIDTH, | |
borderRadius: 0, | |
} | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment