LayoutAnimation
对于一些既定的界面元素,图片、文字、滚动视图等动画效果,我们会优先选择 Animated 来进行处理。但是如果你的动画效果需要改变布局,涉及到其他的 View 或者节点元素。那么就应该优先考虑 LayoutAnimation 来实现了。
较之于 Animated,LayoutAnimation 的使用要更简单。 如果你用 state 记录并改变过某一 View 的高度,那么在此基础上只需要调用一下LayoutAnimation 的动画函数,并保证父节点是 flex 布局的,就可以实现这一高度变化的动画。
动画类型:
- easeInEaseOut() 先加速至全速,再逐渐减速渐停
- linear() 线性动画,保持均速
- spring() 弹性模型,有一个反复回弹的效果
注意:在Android 上使用 LayoutAnimation 需要导入 UIManager 并启用:
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
[动画1 - 改变布局高度,将同级 View 往下推]
效果:
实现:
UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true);
export default class LayoutAnimated extends Component {
state = {
moreHeight: 0, //动态高度
};
_onPress = () => {
LayoutAnimation.spring(); //调用 LayoutAnimation 动画
this.setState({moreHeight: this.state.moreHeight === 0 ? 100 : 0})
}
render() {
return (
<View style={{flex: 1, paddingHorizontal: 20}}>
<View style={styles.card_view}>
<TouchableOpacity onPress={this._onPress}> // 为看到点击效果,不在 Text中实现 onPress
<Text style={styles.more_text}>查看更多</Text>
</TouchableOpacity>
<View style={{height: this.state.moreHeight}}/> //变量直接设定给 View
</View>
<View style={{backgroundColor: '#e29fae', height: 100}}/>
</View>
);
}
}
const styles = StyleSheet.create({
card_view: {backgroundColor: '#ebde5d', marginTop: 20},
more_text: {textAlign: 'right', paddingVertical: 8, borderBottomColor: '#ccc', borderBottomWidth: 1},
});
可见,外部布局是弹性布局的时候,在 setState 的基础上调用一次 LayoutAnimation.spring() ,就可以实现布局改变的动画。
setNativeProps
如果使用的动画,需要频繁地刷新,进行 setState,就必定会影响到性能。 此时可以尝试使用 setNativeProps 来进行修改 ,该方法直接修改基于原生视图的组件的属性来达到效果。
这种直接操作组件的操作并不是应该经常使用的工具。 一般只是用来创建连续的动画,同时避免渲染组件结构和同步太多视图变化所带来的大量开销。 setNativeProps 是一个“简单粗暴”的方法,它直接在底层(DOM、UIView 等)而不是 Rea
ct 组件中记录 state,这样会使代码逻辑难以理清。所以在使用这个方法之前,请尽量先尝试用 setState 和 shouldComponentUpdate 方法来解决问题。
(引用自官方文档)
所以, setNativeProps 是一个强大但也是不得已而为之的选择。
[动画2 - setNativeProps 实现头部栏透明度渐变 ]
有时我们会需要在列表视图滚动的时候,头部栏有一个透明度渐变的效果。在滑动到一定的距离之后,头部栏不再透明。
一般思路是这样,重写 ScrollView 的 onScroll 方法,判断当前所处的 y 值,并通过当前 y 值所处范围计算出应该设置的透明度。但是如果此时用 state 记录透明度,setState 完成设置刷新的话,开销的巨大的,甚至导致一定的卡顿 [onScroll 的触发十分频繁 (scrollEventThro 可以改变 onScroll 调用频率但不推荐)]。所以这里我们应该选择的是 setNativeProps。
实现:
<View //头部view ,设定 ref
ref={head => this.headerView = head}
...
</View>
在滑动的 y 值在(50,100] 之间的时候,设置透明度为 y/100 。这样在 50 以下都是 0.5 半透明, 100 往后透明度都是 1,即不透明。
//ScrollView 的 onScroll 方法
onScroll = ({nativeEvent, contentOffset}) => {
// console.info('nativeEvent', nativeEvent);
const currentY = nativeEvent.contentOffset.y;
if (currentY > 50 && currentY <= 100) {
this.headerView.setNativeProps({
style: {opacity: currentY / 100}
})
}
}
可以看到,setNativeProps 在这个场景中还是非常适用的,如果你体验过 setState,就能很明显的感受到它的优势了。