ReactNativePopup实现示例
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内容请搜索编程宝库以前的文章,希望以后支持编程宝库!
介绍这个项目是一个商城的后台管理系统,用umi2.0搭建,状态管理使用dva,想要实现类似vue keep-alive的效果。具体表现为:从列表页A跳转A的 ...