React Native 官方提供了 Modal
组件,但 Modal
是属于全屏的弹出层,当 Modal
显示时,操作区域只有 Modal
里的元素,而且焦点会被 Modal
劫持。虽然移动端不常见,但有些场景还是希望可以用轻量级一点的 Popup
。
在 React Native 里,元素的层级是不可以被穿透的,子元素无论如何都不能遮挡父元素。所以选择了在顶层添加 Popup
,设置绝对定位,显示时根据指定元素来动态调整 Popup
的位置的方案。
具体实现
Popup
会有显示或隐藏两种状态,使用一个 state
来控制。
const Component = () => {
const [visible, setVisible] = useState(false);
return (
<>
{visible && <></>}
</>
);
};
Popup
的 属于视图类组件,UI 结构包括:
- 一个作为容器的
View
,由于 iOS 有刘海,所以在 iOS 上需要使用SafeAreaView
来避免被刘海遮挡。同时添加一个点击事件监听当点击时关闭Popup
。 - 一个指向目标对象的三角形。
- 一个包裹内容的
View
。
由于 Popup
的位置和内容是动态的,所以需要两个 state
存储相关数据。
- 一个存储位置相关的 CSS。
- 一个存储动态内容。
const Component = ({ style, ...other }) => {
const [visible, setVisible] = useState(false);
const [popupStyle, setPopupStyle] = useState({});
const [content, setContent] = useState(null);
const onPress = useCallback(() => {
setVisible(false);
}, []);
return (
<>
{visible &&
createElement(
Platform.OS === 'ios' ? SafeAreaView : View,
{
style: {
...styles.popup,
...popupStyle,
},
},
<TouchableOpacity onPress={onPress}>
<View style={styles.triangle} />
<View style={{ ...styles.content, ...style }} {...other}>
{content}
</View>
</TouchableOpacity>,
)}
</>
);
};
const styles = StyleSheet.create({
popup: {
position: 'absolute',
zIndex: 99,
shadowColor: '#333',
shadowOpacity: 0.12,
shadowOffset: { width: 2 },
borderRadius: 4,
},
triangle: {
width: 0,
height: 0,
marginLeft: 12,
borderLeftWidth: 8,
borderLeftColor: 'transparent',
borderRightWidth: 8,
borderRightColor: 'transparent',
borderBottomWidth: 8,
borderBottomColor: 'white',
},
content: {
backgroundColor: 'white',
},
});
因为是全局的 Popup
,所以选择了一个全局变量来提供 Popup
相关的操作方法。
如果全局
Popup
不适用,可以改成在需要时插入Popup
并使用ref
来提供操作方法。
目标元素,动态内容和一些相关的可选配置都是在调用 show
方法时通过参数传入的,
useEffect(() => {
global.$popup = {
show: (triggerRef, render, options = {}) => {
const { x: offsetX = 0, y: offsetY = 0 } = options.offset || {};
triggerRef.current.measure((x, y, width, height, left, top) => {
setPopupStyle({
top: top + height + offsetY,
left: left + offsetX,
});
setContent(render());
setVisible(true);
});
},
hide: () => {
setVisible(false);
},
};
}, []);
完整代码
import React, {
createElement,
forwardRef,
useState,
useEffect,
useCallback,
} from 'react';
import PropTypes from 'prop-types';
import {
View,
SafeAreaView,
Platform,
TouchableOpacity,
StyleSheet,
} from 'react-native';
const Component = ({ style, ...other }, ref) => {
const [visible, setVisible] = useState(false);
const [popupStyle, setPopupStyle] = useState({});
const [content, setContent] = useState(null);
const onPress = useCallback(() => {
setVisible(false);
}, []);
useEffect(() => {
global.$popup = {
show: (triggerRef, render, options = {}) => {
const { x: offsetX = 0, y: offsetY = 0 } = options.offset || {};
triggerRef.current.measure((x, y, width, height, left, top) => {
setPopupStyle({
top: top + height + offsetY,
left: left + offsetX,
});
setContent(render());
setVisible(true);
});
},
hide: () => {
setVisible(false);
},
};
}, []);
return (
<>
{visible &&
createElement(
Platform.OS === 'ios' ? SafeAreaView : View,
{
style: {
...styles.popup,
...popupStyle,
},
},
<TouchableOpacity onPress={onPress}>
<View style={styles.triangle} />
<View style={{ ...styles.content, ...style }} {...other}>
{content}
</View>
</TouchableOpacity>,
)}
</>
);
};
Component.displayName = 'Popup';
Component.prototype = {};
const styles = StyleSheet.create({
popup: {
position: 'absolute',
zIndex: 99,
shadowColor: '#333',
shadowOpacity: 0.12,
shadowOffset: { width: 2 },
borderRadius: 4,
},
triangle: {
width: 0,
height: 0,
marginLeft: 12,
borderLeftWidth: 8,
borderLeftColor: 'transparent',
borderRightWidth: 8,
borderRightColor: 'transparent',
borderBottomWidth: 8,
borderBottomColor: 'white',
},
content: {
backgroundColor: 'white',
},
});
export default forwardRef(Component);
使用方法
在入口文件页面内容的末尾插入
Popup
元素。// App.jsx import Popup from './Popup'; const App = () => { return ( <> ... <Popup /> </> ); };
使用全局变量控制。
// 显示 $popup.show(); // 隐藏 $popup.hide();
到此这篇关于React Native Popup实现示例的文章就介绍到这了,更多相关React Native Popup内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!