React Native Popup實(shí)現(xiàn)示例
React Native 官方提供了 Modal 組件,但 Modal 是屬于全屏的彈出層,當(dāng) Modal 顯示時(shí),操作區(qū)域只有 Modal 里的元素,而且焦點(diǎn)會(huì)被 Modal 劫持。雖然移動(dòng)端不常見(jiàn),但有些場(chǎng)景還是希望可以用輕量級(jí)一點(diǎn)的 Popup。
在 React Native 里,元素的層級(jí)是不可以被穿透的,子元素?zé)o論如何都不能遮擋父元素。所以選擇了在頂層添加 Popup,設(shè)置絕對(duì)定位,顯示時(shí)根據(jù)指定元素來(lái)動(dòng)態(tài)調(diào)整 Popup 的位置的方案。
具體實(shí)現(xiàn)
Popup 會(huì)有顯示或隱藏兩種狀態(tài),使用一個(gè) state 來(lái)控制。
const Component = () => {
const [visible, setVisible] = useState(false);
return (
<>
{visible && <></>}
</>
);
};Popup 的 屬于視圖類(lèi)組件,UI 結(jié)構(gòu)包括:
- 一個(gè)作為容器的
View,由于 iOS 有劉海,所以在 iOS 上需要使用SafeAreaView來(lái)避免被劉海遮擋。同時(shí)添加一個(gè)點(diǎn)擊事件監(jiān)聽(tīng)當(dāng)點(diǎn)擊時(shí)關(guān)閉Popup。 - 一個(gè)指向目標(biāo)對(duì)象的三角形。
- 一個(gè)包裹內(nèi)容的
View。
由于 Popup 的位置和內(nèi)容是動(dòng)態(tài)的,所以需要兩個(gè) state 存儲(chǔ)相關(guān)數(shù)據(jù)。
- 一個(gè)存儲(chǔ)位置相關(guān)的 CSS。
- 一個(gè)存儲(chǔ)動(dòng)態(tài)內(nèi)容。
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',
},
});因?yàn)槭侨值?Popup,所以選擇了一個(gè)全局變量來(lái)提供 Popup 相關(guān)的操作方法。
如果全局
Popup不適用,可以改成在需要時(shí)插入Popup并使用ref來(lái)提供操作方法。
目標(biāo)元素,動(dòng)態(tài)內(nèi)容和一些相關(guān)的可選配置都是在調(diào)用 show 方法時(shí)通過(guò)參數(shù)傳入的,
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);使用方法
在入口文件頁(yè)面內(nèi)容的末尾插入
Popup元素。// App.jsx import Popup from './Popup'; const App = () => { return ( <> ... <Popup /> </> ); };使用全局變量控制。
// 顯示 $popup.show(); // 隱藏 $popup.hide();
到此這篇關(guān)于React Native Popup實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)React Native Popup內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
React通過(guò)hook實(shí)現(xiàn)封裝表格常用功能
這篇文章主要為大家詳細(xì)介紹了React通過(guò)hook封裝表格常用功能的使用,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,有需要的小伙伴可以參考下2023-12-12
基于react框架使用的一些細(xì)節(jié)要點(diǎn)的思考
下面小編就為大家?guī)?lái)一篇基于react框架使用的一些細(xì)節(jié)要點(diǎn)的思考。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-05-05
React-View-UI組件庫(kù)封裝Loading加載中源碼
這篇文章主要介紹了React-View-UI組件庫(kù)封裝Loading加載樣式,主要包括組件介紹,組件源碼及組件測(cè)試源碼,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06
React中使用react-player 播放視頻或直播的方法
這篇文章主要介紹了React中使用react-player 播放視頻或直播,本文教大家如何使用react框架及創(chuàng)建實(shí)例的代碼,本文內(nèi)容簡(jiǎn)短給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-01-01
Input標(biāo)簽自動(dòng)校驗(yàn)功能去除實(shí)現(xiàn)
這篇文章主要為大家介紹了Input標(biāo)簽的自動(dòng)拼寫(xiě)檢查功能去除實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
react 可拖拽進(jìn)度條的實(shí)現(xiàn)
本文主要介紹了react 可拖拽進(jìn)度條的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-04-04
聊聊ant?design?charts?獲取后端接口數(shù)據(jù)展示問(wèn)題
今天在做項(xiàng)目的時(shí)候遇到幾個(gè)讓我很頭疼的問(wèn)題,一個(gè)是通過(guò)后端接口成功訪問(wèn)并又返回?cái)?shù)據(jù),但拿不到數(shù)據(jù)值。其二是直接修改state中的data,console中數(shù)組發(fā)生變化但任然數(shù)據(jù)未顯示,這篇文章主要介紹了ant?design?charts?獲取后端接口數(shù)據(jù)展示,需要的朋友可以參考下2022-05-05

