UNPKG

@uiw/react-native

Version:
216 lines (197 loc) 5.12 kB
import React, { Component, Fragment } from 'react'; import { Animated, StyleSheet, TouchableOpacity, Dimensions } from 'react-native'; const DEVICE_WIDTH = Dimensions.get('window').width; const DEVICE_HEIGHT = Dimensions.get('window').height; export default class Drawer extends Component { static defaultProps = { placement: 'left', drawerBackgroundColor: '#fff', drawerWidth: 300, drawerHeight: 500, maskClosable: true, isOpen: false, onChange: () => null, openDrawer: () => null, closeDrawer: () => null }; constructor(props) { super(props); this.state = { zIndexValue: 0, overlayValue: new Animated.Value(0), drawerValue: new Animated.ValueXY({ ...this.getInitPosition() }) }; } componentDidMount() { if (this.props.isOpen) { this.openDrawer(); } } UNSAFE_componentWillReceiveProps(nextProps) { if (nextProps.isOpen !== this.props.isOpen) { this.handleDrawer(!!nextProps.isOpen); } } onOverlayClick = e => { const { maskClosable } = this.props; if (!maskClosable) { return false; } e.stopPropagation(); this.closeDrawer(); }; render() { const { isOpen, style, drawerWidth, drawerBackgroundColor, maskProps, placement, drawerHeight } = this.props; const { drawerValue, overlayValue, zIndexValue } = this.state; const isTopOrBottom = placement === 'top' || placement === 'bottom'; const changeStyle = isTopOrBottom ? 'height' : 'width'; const dynamicDrawerStyles = { backgroundColor: drawerBackgroundColor }; if (isTopOrBottom) { dynamicDrawerStyles.top = placement === 'top' ? 0 : null; dynamicDrawerStyles.bottom = placement === 'bottom' ? 0 : null; dynamicDrawerStyles.height = drawerWidth; dynamicDrawerStyles.width = '100%'; } else { dynamicDrawerStyles.left = placement === 'left' ? 0 : null; dynamicDrawerStyles.right = placement === 'right' ? 0 : null; dynamicDrawerStyles.width = drawerWidth; } const overlayOpacity = overlayValue.interpolate({ inputRange: [0, 1], outputRange: [0, 0.3], extrapolate: 'clamp' }); return <Fragment> <Animated.View style={[styles.drawer, dynamicDrawerStyles, style, // eslint-disable-next-line react-native/no-inline-styles { [changeStyle]: isOpen ? isTopOrBottom ? drawerHeight : drawerWidth : 0, transform: [{ translateX: drawerValue.x }, // x轴移动 { translateY: drawerValue.y } // y轴移动 ] }]}> {this.props.children} </Animated.View> <Animated.View pointerEvents={isOpen ? 'auto' : 'none'} style={[styles.overlay, styles.positionFull, maskProps, { // opacity: overlayValue, opacity: overlayOpacity, zIndex: zIndexValue }]}> <TouchableOpacity style={[styles.positionFull, // eslint-disable-next-line react-native/no-inline-styles { zIndex: 3003, position: 'absolute' }]} onPress={this.onOverlayClick.bind(this)} /> </Animated.View> </Fragment>; } handleDrawer(isOpen) { isOpen ? this.openDrawer() : this.closeDrawer(); } getInitPosition() { const { drawerWidth, placement, drawerHeight } = this.props; const xy = { x: 0, y: 0 }; if (placement === 'left') { xy.x = -(drawerWidth || 0); } if (placement === 'right') { xy.x = DEVICE_WIDTH || 0; } if (placement === 'top') { xy.y = -(drawerHeight || 0); } if (placement === 'bottom') { xy.y = DEVICE_HEIGHT || 0; } return xy; } openDrawer() { this.setState({ zIndexValue: 3002 }); Animated.parallel([Animated.spring(this.state.drawerValue, { toValue: { x: 0, y: 0 }, overshootClamping: true, useNativeDriver: true }), Animated.spring(this.state.overlayValue, { toValue: 0.7, overshootClamping: true, useNativeDriver: true })]).start(() => { this.props.openDrawer(true); this.props.onChange(true); }); } closeDrawer() { const { drawerValue, overlayValue } = this.state; Animated.parallel([Animated.spring(drawerValue, { toValue: { ...this.getInitPosition() }, overshootClamping: true, useNativeDriver: true }), Animated.spring(overlayValue, { toValue: 0, overshootClamping: true, useNativeDriver: true })]).start(() => { this.props.closeDrawer(false); this.props.onChange(false); this.setState({ zIndexValue: 0 }); }); } } const styles = StyleSheet.create({ drawer: { position: 'absolute', top: 0, bottom: 0, flex: 1, zIndex: 3004 }, positionFull: { position: 'absolute', top: 0, bottom: 0, left: 0, right: 0 }, overlay: { backgroundColor: '#000', zIndex: 3002 } });