如何在React Native中基于React Router 4实现导航抽屉

时间:2018-09-04 08:17:21

标签: react-native

我想创建一个类似于uber的导航抽屉。那么,有没有我可以用来基于React Router 4开发应用导航抽屉的库或软件包?

1 个答案:

答案 0 :(得分:0)

使用此

https://github.com/Tinysymphony/react-native-drawer-menu

https://github.com/root-two/react-native-drawer

您可以自己创建视图,这并不容易,就像这样:

import React, { PureComponent } from 'react';
import {
  View,
  Animated,
  Easing,
  Dimensions,
  TouchableOpacity,
  TouchableWithoutFeedback
} from 'react-native';
import mitt from 'mitt';
import styled from 'styled-components';

import { t } from '../i18n';
import { TOOLBAR_HEIGHT } from './Toolbar';
import Avatar from '../components/Avatar';
import Icon from '../components/Icon';
import Text from './Text';

const { width } = Dimensions.get('window');

//-----------------------------------------------
class MenuDrawer extends PureComponent {
  static defaultProps = {
    width: (width * 75) / 100
  };

  constructor(props) {
    super(props);
    this.state = { show: false };
    this.emitter = mitt();
    this.animateValue = {
      opacity: new Animated.Value(0),
      marginLeft: new Animated.Value(-props.width)
    };
  }

  animation({ opacity, mgleft }) {
    this.animateValue.opacity.setValue(opacity.start);
    this.animateValue.marginLeft.setValue(mgleft.start);

    return Animated.parallel([
      Animated.timing(this.animateValue.opacity, {
        toValue: opacity.end,
        duration: 100,
        easing: Easing.linear,
        useNativeDriver: true
      }),
      Animated.timing(this.animateValue.marginLeft, {
        toValue: mgleft.end,
        duration: 100,
        easing: Easing.linear,
        useNativeDriver: true
      })
    ]);
  }

  subscribe = cb => {
    this.emitter.on('state-change', status => {
      cb(status);
    });
  };

  show = () => {
    this.emitter.emit('state-change', true);

    this.setState({ ...this.state, show: true }, () => {
      const opacity = { start: 0, end: 0.6 };
      const mgleft = { start: this.props.width, end: 0 };
      this.animation({ opacity, mgleft }).start();
    });
  };

  hide = () => {
    this.emitter.emit('state-change', false);

    const opacity = { start: 0.6, end: 0 };
    const mgleft = { start: 0, end: this.props.width };
    this.animation({ opacity, mgleft }).start(({ finished }) => {
      if (finished) {
        this.setState({ ...this.state, show: false });
      }
    });
  };
  handlePressOnBackground = () => {
    this.hide();
  };

  renderContent() {
    const { me } = this.props;
    const name = me.fullname;
    const avatar_url = me.avatar_url;

    return (
      <StyledView>
        <StyledMyInfo>
          <Avatar size={96} uri={avatar_url} name={name} isAgent={true} />
          <StyledName text={name} />
        </StyledMyInfo>
        <StyledMenu>
          <TouchableOpacity onPress={this.props.handlePressSettings}>
            <StyledMenuItem>
              <StyledIcon>
                <Icon size={20} source={require('../assets/setting.png')} />
              </StyledIcon>
              <StyledLabel text={t('general_settings')} />
            </StyledMenuItem>
          </TouchableOpacity>
          <TouchableOpacity onPress={this.props.handlePressChangePassword}>
            <StyledMenuItem>
              <StyledIcon>
                <Icon
                  size={20}
                  ml={2}
                  source={require('../assets/password.png')}
                />
              </StyledIcon>
              <StyledLabel text={t('profile_language_change_password_link')} />
            </StyledMenuItem>
          </TouchableOpacity>
          <TouchableOpacity onPress={this.props.handlePressLogout}>
            <StyledMenuItem>
              <StyledIcon>
                <Icon
                  size={20}
                  ml={-1}
                  source={require('../assets/logout.png')}
                />
              </StyledIcon>
              <StyledLabel text={t('agent_profile_logout')} />
            </StyledMenuItem>
          </TouchableOpacity>
        </StyledMenu>
      </StyledView>
    );
  }

  render() {
    const { id } = this.props.me;
    if (!this.state.show || !id) return null;

    return (
      <StyledViewContainer>
        <TouchableWithoutFeedback
          style={{ flex: 1 }}
          onPress={this.handlePressOnBackground}
        >
          <Animated.View
            style={[
              {
                position: 'absolute',
                top: 0,
                left: 0,
                bottom: 0,
                right: 0,
                backgroundColor: 'black'
              },
              { opacity: this.animateValue.opacity }
            ]}
          />
        </TouchableWithoutFeedback>
        <Animated.View
          style={[
            {
              position: 'absolute',
              top: 0,
              left: 0,
              bottom: 0,
              width: '75%'
            },
            { marginLeft: -this.animateValue.marginLeft }
          ]}
        >
          {this.renderContent()}
        </Animated.View>
      </StyledViewContainer>
    );
  }
}

//-----------------------------------------------
const StyledViewContainer = styled(View)`
  position: absolute;
  top: ${TOOLBAR_HEIGHT};
  right: 0;
  bottom: 0;
  left: 0;
`;
const StyledView = styled(View)`
  flex: 1;
  background-color: #f9f9f9;
`;
const StyledMyInfo = styled(View)`
  justify-content: center;
  align-items: center;
  height: 240;
  background-color: white;
`;
const StyledName = styled(Text)`
  margin-top: 10px;
  font-size: ${props => props.theme.fontSizeLarge};
  font-family: ${props => props.theme.fontSemiBold};
`;
const StyledMenu = styled(View)`
  padding: 12px 15px;
`;
const StyledMenuItem = styled(View)`
  margin-bottom: 5px;
  flex-direction: row;
  align-items: center;
`;
const StyledIcon = styled(View)`
  width: 40;
  height: 40;
  justify-content: center;
  align-items: center;
`;
const StyledLabel = styled(Text)``;
export default MenuDrawer;