知识点:
- 首页动画
- 动画组件说明
- 动画优化
首页加入动画
我们在首页加入这样一个动画,在默认情况下首页顶部是透明的,当内容往下滚动的时候,将顶部的背景颜色变成白色,搜索框颜色变成其他颜色。
在首页的 state 中加入默认的位置值,这里使用动画库的单项值,设置默认值为 0。
scrollTop: new Animated.Value(0)
修改 FlatList 组件的滚动监听频率,这里的意思是每秒触发 100 次监听方法,即等于每 10 毫秒触发一次方法。
scrollEventThrottle={100}
修改监听方法,如果滚动超过 200 就不再做任何操作,这里使用直接设置值的方式,滚动的数字变化特别频繁,已经不需要使用动画的方式改变值了。
//滚动监听
_onScroll(e) {
const y = e.contentOffset.y;
if (y < 200) this.state.scrollTop.setValue(y)
}
将顶部的组件外层替换使用 Animated.View,这里做了一个映射,将数字直接映射成对应的颜色透明度。注意不要使用 opacity,该属性会把内容也变成透明的,这里同时映射负数是因为列表在下拉刷新的时候会变成负数,也要将这个因素也考虑进来。
<Animated.View style={[styles.headerView,{
backgroundColor: this.state.scrollTop.interpolate({
inputRange: [-100, 0, 100],
outputRange: ['rgba(255,255,255,.5)', 'rgba(255,255,255,0)', 'rgba(255,255,255,1)']
})
}]}>
<SearchHeader share={() => this.share()} navigation={this.props.navigation} />
</Animated.View>
回到顶部
在监听滚动条的基础上可以直接做滚动到顶部的判断,如果滚动距离超过 400 并且回到顶部按钮是隐藏的,直接显示按钮,反之则把按钮隐藏起来。
在渲染方法中加入一个可点击的图片,设置点击滚动到最顶层。
{this.state.showTop && <TouchableOpacity onPress={()=>this.toTop()}>
<Image style={styles.toBtn}
source={{ uri: require("../images/icon-to-top") }} />
</TouchableOpacity>}
修改监听方法,让滚动的时候可以根据情况改变按钮的显示隐藏,这里加上判断可以减少很多不必要的状态设置。
//滚动监听
_onScroll(e) {
const y = e.contentOffset.y;
if (y < 200) this.state.scrollTop.setValue(y)
if (y < 500 && this.state.showTop) {
this.setState({ showTop: false })
}
if (y > 500 && !this.state.showTop) {
this.setState({ showTop: true })
}
}
添加回到顶部的方法:
//回到顶部
toTop(){
this.refs.flatlist.scrollToOffset({offset:0})
}
其他动画
顶部颜色变换只是一个很简单的动画,有兴趣的同学可以试试加入更多的变化,比如通过叠加的两个图片造成一个图片的变色动画,以及字体的颜色、阴影(提示:不能用阴影属性)动画等,这些都需要靠各位的聪明才智来实现了。
动画组件说明
RN 自带的 Animated 组件提供了几个已经处理过的组件,在使用动画的时候必须使用处理过的组件,不然是不支持的。虽然 RN 也提供了 createAnimatedComponent 方法来自定义组件,但是尽量还是不要使用的好。
- Animated.Image
- Animated.ScrollView
- Animated.Text
- Animated.View
创建动画组件能够接收的值必须使用下面其中之一初始化变量。动画组件会根据配置动态的改变值的内容,同时这个变化也会体现在 UI 界面上,不需要使用 set 方式去改变。
- Animated.Value() //改变单个值
- Animated.ValueXY() //改变两个值
最常用的是 timing 方法,该方法根据时间动态生成最终的结果,比如点击之后:
Animated.timing(value/*要改变的值*/,{
//配置
duration:500,//动画持续时间
easing:Easing.inOut(Easing.ease),//动画方式,需要引入 Easing
delay:0,//开始动画之前的延迟,默认0
useNativeDriver:false//是否使用原生方式动画,默认不实用
}).start();//最后调用start启动动画
设置好的变量还可以直接调用它上面的方法,比如停止动画、改变值等操作。
this.state.anim.setValue(1);//直接设置值
this.state.anim.stopAnimation(callback?);//停止动画,参数是停止之后的回调
this.state.anim.addListener(callback);//监听值的变化
this.state.anim.setOffset(1);//动画偏移,每次计算都会加上这个值
this.state.anim.flattenOffset();//清除偏移
要使用动画做效果,最好使用 Transform 下面的值来生成最终的结果,Transform 下面通常支持 scale、scaleX、scaleY、translateX、translateY、rotate、rotateX、rotateY、rotateZ 这几个属性。
如果要设置颜色、角度等字符串型的值就要使用到插值函数 interpolate,该方法可以将对应的值映射为最终的结果,如果不在映射区间的则不会有改动。
//设置背景色
backgroundColor: this.state.scrollTop.interpolate({
inputRange: [0, 100],
outputRange: ['rgba(255,255,255,0)', 'rgba(255,255,255,1)']
})
//设置角度
transform:[
{rotate: this.state.rotateValue.interpolate({
inputRange: [0, 1],
outputRange: ['0deg', '360deg']})}
]
除了上面的简单动画,也可以使用 RN 的方法将几个动画组合使用。
- Animated.delay(time);//延迟毫秒执行动画
- Animated.parallel([],config); //同时开始执行动画
- Animated.sequence([]); //一次执行动画
下面是一个顺序执行动画的例子。
startAnimation() {
this.state.bounceValue.setValue(1.5); // 设置一个较大的初始值
this.state.fadeOutOpacity.setValue(1);
Animated.sequence([
// 基础的单次弹跳物理模型
Animated.spring(
this.state.bounceValue, {
toValue: 0.8,
friction: 1,// 摩擦力,默认为7.
tension: 40,// 张力,默认40。
}),
//渐隐动画
Animated.timing(this.state.fadeOutOpacity, {
toValue: 0,
duration: 2000,
easing: Easing.linear,
})
]).start();
}
优化建议
写动画的地方很有可能会引起 UI 的频繁刷新,在做动画的时候要注意一点这个。
如果使用定时器控制动画,请在销毁组件的时候清除定时器。
尽量使用 Transfrom 的属性来做动画的效果,其他的样式属性不如这个效率高。
尽量直接改变要动画的节点,要记得使用 Animted 提供的组件来做。
可以在动画的 start 中回调自身达成循环动画,也可以使用定时器定时重启动画。
在写这篇文章的时候 App 联盟出了一个快应用,这个模式还是很不错的,在未来有很大的潜力,如果要类比,可以看做是一个环境更大的小程序吧,对于快速开发上线的中小项目都非常的有利。