因为业务需要实现侧滑抽屉菜单,看了一眼项目中封装的DrawerLayout
组件,便直接引用准备使用,但是使用后却发现并没有理想中的动画效果,便打开组件代码看了一眼,发现之前的作者是直接用Modal
写的,然后在modal里写了一个蒙层和一个View
容器: 大概布局为:
<Modal>
<Animated.View>
<View>{此处作为蒙层,点击关闭抽屉用}</View>
</Animated.View>
<View>{this.props.children}</View>
</Modal>
总之组件似乎写法有问题,只有弹框没有动画效果,改吧改吧应该就可以了,于是开始修改了的过程,经过了半个小时的努力,总算是改好了,但是动画效果并不是我预想中的样子,于是决定放弃使用项目中别人写的组件。
日常打开我们的老朋友,度娘,搜索了一下React-native 抽屉组件,要么是让安装第三方的库,要么是一些涉及到原生应用的帖子(表示看不懂),要么还有一些收费的帖子,一无所获,干脆决定自己写吧,这么简单的功能就是怕麻烦,想找个现成的粘贴得了,奈何不允许我偷懒。废话不多说,直接码代码:
/**
* @Author: small_axe
* @描述: 抽屉动画
* @Date: 2021/05/26
*/
import React, {useState, useEffect, useCallback} from 'react';
import {Animated, StyleSheet, Dimensions} from 'react-native';
import PropTypes from 'prop-types';
const WINDOW = Dimensions.get('window');
const AWSDrawerMenu = props => {
const {visible, duration, onShow, dismiss, menuPosition} = props;
const [animateValue, setAnimateValue] = useState(new Animated.Value(0));
useEffect(() => {
if (visible) {
_onShow();
} else {
_dismiss();
}
}, [_onShow, visible, _dismiss]);
// eslint-disable-next-line react-hooks/exhaustive-deps
const _onShow = () => {
if (onShow) {
onShow();
}
animate(1);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
const _dismiss = () => {
if (dismiss) {
dismiss();
}
animate(0);
};
// 添加动画效果
const animate = toValue => {
Animated.timing(animateValue, {
toValue: toValue,
duration: duration,
useNativeDriver: true,
friction: 9,
}).start();
};
// 判断position从不同位置唤出抽屉
const getPosition = useCallback(() => {
switch (menuPosition) {
case 'left':
return {
translateX: animateValue.interpolate({
inputRange: [0, 1],
outputRange: [-WINDOW.width, 0],
}),
};
case 'right':
return {
translateX: animateValue.interpolate({
inputRange: [0, 1],
outputRange: [WINDOW.width, 0],
}),
};
case 'top':
return {
translateY: animateValue.interpolate({
inputRange: [0, 1],
outputRange: [-WINDOW.height, 0],
}),
};
case 'bottom':
return {
translateY: animateValue.interpolate({
inputRange: [0, 1],
outputRange: [WINDOW.height, 0],
}),
};
default:
return {
translateX: animateValue.interpolate({
inputRange: [0, 1],
outputRange: [-WINDOW.width, 0],
}),
};
}
}, [animateValue, menuPosition]);
return (
<Animated.View
style={[
styles.container,
{
transform: [getPosition()],
},
]}>
{props.children}
</Animated.View>
);
};
AWSDrawerMenu.propTypes = {
visible: PropTypes.bool, // 控制抽屉显影状态
duration: PropTypes.number, // 动画持续时间
onShow: PropTypes.func, // 显示
dismiss: PropTypes.func, // 影藏
menuPosition: PropTypes.oneOf(['left', 'right', 'top', 'bottom']), // 抽屉出现的位置
};
AWSDrawerMenu.defaultProps = {
visible: false,
duration: 500,
onShow: () => {},
dismiss: () => {},
menuPosition: 'left',
};
module.exports = AWSDrawerMenu;
const styles = StyleSheet.create({
container: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0,
zIndex: 1000,
backgroundColor: '#fff',
},
});
大功告成,然后就是应用走一波,很好的满足了菜单从各个方向实现抽屉动画的效果。
只是简单写了一个实现抽屉效果的动画容器,使用的时候用此组件包裹你的内容组件,就可实现抽屉的动画效果。觉得有帮到你的同学记得点个赞。