欢迎你们Star咱们的仓库react-native-showcase,记录了各类React Native的交互、动画效果。javascript
本文介绍如何实现利用共享UI元素的动画,实现相似苹果App Store的Today页面的动画效果,咱们先看最终的效果:前端
UI布局很简单,就是一个普通的ScrollView,咱们直接看代码:java
<ScrollView style={{ flex: 1, paddingTop: 20, }}>
{
Images.map((image, index) => {
return (
<View style={{ width: SCREEN_WIDTH, height: SCREEN_HEIGHT - 150, padding: 15 }}> <Image source={image} style={{ height: null, width: null, borderRadius: 20, flex: 1, resizeMode: 'cover', }} /> </View> ); }) } </ScrollView>
复制代码
也能够直接打开expo project查看第一步效果。react
在开始写代码以前咱们先分先一下动画的几个特色:git
首先咱们要明确从列表"页"到详情"页",其实并非真正意义的页面跳转(至少现有的不管是react-navigation仍是react-native-navigation等都没法实现这种复用页面UI元素的跳转动画),所以所谓的列表"页"、详情"页"只是同一个页面的两种不一样状态的Viewgithub
有了上面的结论,咱们在本身分析动画效果:其实整个打开详情动画有两部分组成:react-native
1. 详情"页"图片从当前点击位置和大小移动到最终顶部位置
2. 在此过程当中图片介绍文案位置从屏幕视觉外移动到最终位置
复制代码
下面咱们来看看具体代码如何实现:app
首先完成详情"页"布局:布局
<ScrollView style={{ flex: 1, paddingTop: 20, }}>
...
</ScrollView>
<View style={StyleSheet.absoluteFill}>
<View style={{ flex: 2 }}>
<Image
source={ this.state.activeImage ? this.state.activeImage : null }
style={{ top: 0, right: 0, height: null, width: null, resizeMode: 'cover' }}
/>
<TouchableWithoutFeedback>
<View style={{ position: 'absolute', right: 20, top: 30 }}>
<Text style={{ fontSize: 20, color: '#fff' }}>X</Text>
</View>
</TouchableWithoutFeedback>
</View>
<View style={{ flex: 1, backgroundColor: '#fff', }}>
<Text style={{ fontSize: 24 }}>这里是图片的title</Text>
<Text>这里是图片的详情,详细介绍图片的状况</Text>
</View>
</View>
复制代码
咱们先隐藏列表页,详情页以下图所示:post
接下来咱们须要给:图片、文字介绍内容区域、关闭按钮等分别添加动画效果。
图片动画效果分析:当用户点击列表图片时,从用户当前点击图片的位置、大小变化到最终详情"页"中图片位置和大小。
咱们能够利用当前点击图片Ref的measure方法中拿到它的位置和大小信息,代码以下:
constructor(props) {
// 存贮全部的图片ref,动画时获取当前图片的位置信息
this.imageRef = [];
this.state = {
activeImage: null,
}
}
function openImage(index) {
this.imageRef[index].measure((x, y, width, height, pageX, pageY) => {
// pageX、pageY 图片在屏幕中的坐标位置
// width、height 图片的宽高
// ...... 有了图片的位置和大小信息咱们就能够开始动画了
});
}
<TouchableWithoutFeedback onPress={() => { this.openImage(index); }}>
<Image source={image} ref={(image) => { this.imageRef[index] = image; }} style={{ height: null, width: null, borderRadius: 20, flex: 1, resizeMode: 'cover', }} /> </TouchableWithoutFeedback>
复制代码
下面咱们来完成图片的动画效果:位置、大小、圆角等变化:
constructor(props) {
this.position = new Animated.ValueXY();
this.measure = new Animated.ValueXY();
this.animation = new Animated.Value(0);
}
function openImage(index) {
// 获取点击图片的信息
this.imageRef[index].measure((x, y, width, height, pageX, pageY) => {
this.position.setValue({ x: pageX, y: pageY });
this.measure.setValue({ x: width, y: height });
this.setState(
() => { return { activeImage: Images[index] } },
() => {
// 获取目标位置信息
this.imageContainer.measure((x, y, width, height, pageX, pageY) => {
Animated.parallel([
Animated.timing(this.position.x, {
toValue: pageX,
duration: 350,
// 增长一个弹性效果
easing: Easing.back(1),
}),
Animated.timing(this.position.y, {
toValue: pageY,
duration: 350,
easing: Easing.back(1),
}),
Animated.timing(this.measure.x, {
toValue: width,
duration: 350,
easing: Easing.back(1),
}),
Animated.timing(this.measure.y, {
toValue: height,
duration: 350,
easing: Easing.back(1),
}),
Animated.timing(this.animation, {
toValue: 1,
duration: 350,
easing: Easing.back(1),
}),
]).start();
});
}
);
});
}
const imageBorderRadiusAnimation = this.animation.interpolate({
inputRange: [0, 1],
outputRange: [20, 0]
});
const imageAnimationStyle = {
left: this.position.x,
top: this.position.y,
width: this.measure.x,
height: this.measure.y,
borderRadius: imageBorderRadiusAnimation,
};
<TouchableWithoutFeedback onPress={() => { this.openImage(index); }}> <Image source={image} ref={(image) => { this.imageRef[index] = image; }} style={[ { height: null, width: null, borderRadius: 20, flex: 1, resizeMode: 'cover', }, imageAnimationStyle } /> </TouchableWithoutFeedback> 复制代码
这样咱们就完成了图片的动画效果,一样添加文字区域和关闭按钮的动画效果,完整代码能够参考这里。
其实关闭详情动画彻底就是打开详情动画的反转:
代码和打开详情比较相似,能够直接参考最终版
欢迎你们star咱们的人人贷大前端团队博客,全部的文章还会同步更新到知乎专栏 和 掘金帐号,咱们每周都会分享几篇高质量的大前端技术文章。