当前位置: 首页 > 工具软件 > Top Drawer > 使用案例 >

【React-Native】自定义Drawer(抽屉)动画组件

林俊英
2023-12-01

自己实现一个简单的抽屉组件


因为业务需要实现侧滑抽屉菜单,看了一眼项目中封装的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',
  },
});

大功告成,然后就是应用走一波,很好的满足了菜单从各个方向实现抽屉动画的效果。

只是简单写了一个实现抽屉效果的动画容器,使用的时候用此组件包裹你的内容组件,就可实现抽屉的动画效果。觉得有帮到你的同学记得点个赞。

 类似资料: