Skip to content

Instantly share code, notes, and snippets.

@fiznool
Last active July 14, 2021 22:01
Show Gist options
  • Save fiznool/db2ea07e9780c3d2d1c751153370b88e to your computer and use it in GitHub Desktop.
Save fiznool/db2ea07e9780c3d2d1c751153370b88e to your computer and use it in GitHub Desktop.
// Inspired by https://stackoverflow.com/a/50523132/1171775
import React, { Component } from 'react';
import {
Animated,
StatusBar,
StyleSheet,
Text,
View,
} from 'react-native';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
const HEADER_MAX_HEIGHT = 300;
const HEADER_MIN_HEIGHT = 80;
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(0),
};
}
_renderScrollViewContent() {
const data = Array.from({ length: 30 });
return (
<View style={styles.scrollViewContent}>
{data.map((_, i) => (
<View key={i} style={styles.row}>
<Text>{i}</Text>
</View>
))}
</View>
);
}
render() {
const { scrollY } = this.state;
const headerTranslate = scrollY.interpolate({
inputRange: [0, HEADER_SCROLL_DISTANCE],
outputRange: [0, -HEADER_SCROLL_DISTANCE],
extrapolate: 'clamp',
});
const headerBounce = scrollY.interpolate({
inputRange: [-HEADER_SCROLL_DISTANCE, 0],
outputRange: [1.5, 1],
extrapolate: 'clamp',
});
const imageOpacity = scrollY.interpolate({
inputRange: [0, HEADER_SCROLL_DISTANCE * 0.75, HEADER_SCROLL_DISTANCE],
outputRange: [1, 1, 0],
extrapolate: 'clamp',
});
const imageTranslate = scrollY.interpolate({
inputRange: [0, HEADER_SCROLL_DISTANCE],
outputRange: [0, 100],
extrapolate: 'clamp',
});
return (
<SafeAreaProvider>
<SafeAreaView edges={['bottom']} style={styles.fill}>
<StatusBar
translucent
barStyle="light-content"
backgroundColor="rgba(0, 0, 0, 0.251)"
/>
<Animated.View
pointerEvents="none"
style={[
styles.header,
{ transform: [{ translateY: headerTranslate }, { scale: headerBounce }] },
]}
>
<Animated.Image
style={[
styles.headerImage,
{
opacity: imageOpacity,
transform: [{ translateY: imageTranslate }],
},
]}
source={{uri: 'https://i2.wp.com/beebom.com/wp-content/uploads/2016/01/Reverse-Image-Search-Engines-Apps-And-Its-Uses-2016.jpg?resize=640%2C426'}}
/>
</Animated.View>
<Animated.ScrollView
style={styles.fill}
scrollEventThrottle={1}
onScroll={Animated.event(
[{ nativeEvent: { contentOffset: { y: this.state.scrollY } } }],
{ useNativeDriver: true },
)}
>
{this._renderScrollViewContent()}
</Animated.ScrollView>
</SafeAreaView>
</SafeAreaProvider>
);
}
}
const styles = StyleSheet.create({
fill: {
flex: 1,
},
content: {
flex: 1,
},
header: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
backgroundColor: 'white',
overflow: 'hidden', // Important so that content overlays image
height: HEADER_MAX_HEIGHT, // Begins at max height
zIndex: 10,
},
headerImage: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
width: null,
height: HEADER_MAX_HEIGHT, // Begins at max height
resizeMode: 'cover',
},
scrollViewContent: {
paddingTop: HEADER_MAX_HEIGHT,
},
row: {
height: 40,
margin: 16,
backgroundColor: '#D3D3D3',
alignItems: 'center',
justifyContent: 'center',
},
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment