Created
March 23, 2018 16:26
-
-
Save shengoo/d9f77764ae2e0b49927584e3e237ae95 to your computer and use it in GitHub Desktop.
HeaderTransition
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, | |
ScrollView, | |
Image, | |
} from 'react-native'; | |
const HEADER_MAX_HEIGHT = 160; | |
const HEADER_MIN_HEIGHT = 64; | |
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, | |
), | |
}; | |
} | |
_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() { | |
// 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 headerHeight = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE], | |
outputRange: [HEADER_MAX_HEIGHT, HEADER_MIN_HEIGHT], | |
extrapolate: 'clamp', | |
}); | |
//图片变成透明的 | |
const imageOpacity = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE], | |
outputRange: [1, 0], | |
extrapolate: 'clamp', | |
}); | |
//图片跟随滚动,但是是滚动距离的一般 | |
const imageTranslate = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE], | |
outputRange: [0, HEADER_SCROLL_DISTANCE / 2], | |
extrapolate: 'clamp', | |
}); | |
//标题颜色 | |
const titleColor = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE], | |
outputRange: ['#fff', '#000'], | |
extrapolate: 'clamp', | |
}); | |
//头像大小 | |
const avatarSize = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE], | |
outputRange: [80, 24], | |
extrapolate: 'clamp', | |
}); | |
//头像圆角 | |
const avatarBorderRadius = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE], | |
outputRange: [0, 12], | |
extrapolate: 'clamp', | |
}); | |
//头像移动距离 | |
const avatarTranslate = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE], | |
outputRange: [-80, 0], | |
extrapolate: 'clamp', | |
}); | |
const titleTranslateX = scrollY.interpolate({ | |
inputRange: [0, HEADER_SCROLL_DISTANCE], | |
outputRange: [-40, 0], | |
extrapolate: 'clamp', | |
}); | |
return ( | |
<View style={styles.fill}> | |
<StatusBar | |
barStyle="dark-content" | |
/> | |
<Animated.ScrollView | |
style={styles.fill} | |
scrollEventThrottle={1} | |
onScroll={Animated.event( | |
[{nativeEvent: {contentOffset: {y: this.state.scrollY}}}], | |
)} | |
contentInset={{ | |
top: HEADER_MAX_HEIGHT, | |
}} | |
contentOffset={{ | |
y: -HEADER_MAX_HEIGHT, | |
}} | |
> | |
{this._renderScrollViewContent()} | |
</Animated.ScrollView> | |
<Animated.View | |
style={[ | |
styles.header, | |
{transform: [{translateY: headerTranslate}]}, | |
]} | |
> | |
<Animated.Image | |
style={[ | |
styles.backgroundImage, | |
{ | |
opacity: imageOpacity, | |
transform: [{translateY: imageTranslate}], | |
}, | |
]} | |
source={require('./assets/cat.jpg')} | |
/> | |
</Animated.View> | |
<Animated.View | |
style={[ | |
styles.bar, | |
{ | |
height: headerHeight, | |
}, | |
]} | |
> | |
<Animated.Image style={[ | |
{width: avatarSize, height: avatarSize, borderRadius: avatarBorderRadius,}, | |
{ | |
transform: [ | |
{translateX: avatarTranslate}, | |
], | |
}, | |
]} source={require('./assets/avatar.jpg')}/> | |
<Animated.Text style={[ | |
styles.title, | |
{ | |
color: titleColor, | |
transform: [ | |
{translateX: titleTranslateX}, | |
// {translateY: titleTranslateY}, | |
], | |
} | |
]}>My Name</Animated.Text> | |
</Animated.View> | |
</View> | |
); | |
} | |
} | |
const styles = StyleSheet.create({ | |
fill: { | |
flex: 1, | |
}, | |
content: { | |
flex: 1, | |
}, | |
header: { | |
position: 'absolute', | |
top: 0, | |
left: 0, | |
right: 0, | |
backgroundColor: '#f5f5f6', | |
overflow: 'hidden', | |
height: HEADER_MAX_HEIGHT, | |
}, | |
backgroundImage: { | |
position: 'absolute', | |
top: 0, | |
left: 0, | |
right: 0, | |
width: null, | |
height: HEADER_MAX_HEIGHT, | |
resizeMode: 'cover', | |
}, | |
bar: { | |
// backgroundColor: 'transparent', | |
paddingTop: Platform.OS === 'ios' ? 20 : 0, | |
height: 44, | |
alignItems: 'center', | |
justifyContent: 'center', | |
position: 'absolute', | |
top: 0, | |
left: 0, | |
right: 0, | |
flexDirection: 'row', | |
borderBottomWidth: StyleSheet.hairlineWidth, | |
borderBottomColor: '#C6C7CB', | |
}, | |
title: { | |
// color: 'white', | |
color: '#191A1C', | |
marginLeft: 8, | |
fontSize: 18, | |
}, | |
scrollViewContent: { | |
// iOS uses content inset, which acts like padding. | |
paddingTop: Platform.OS !== 'ios' ? HEADER_MAX_HEIGHT : 0, | |
}, | |
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