如何将MUI Modal居中于指定div而非整个屏幕?
问题:MUI Modal 无法在指定容器内居中,总是以屏幕为基准居中
我尝试将MUI Modal居中到class为check-mate的div容器内,但它自动居中于整个屏幕。我希望仅在该div内居中,目前无法将Modal与该div关联,求解决方法。
File.js
<React.Fragment> <div className="check-mate" style={{ backgroundColor: 'red', position: 'relative', }} > <> <Stack spacing={2} className="absolute right-0 mt-10 mr-10" > <Fab id="zoom_in" tooltip="Zoom In" size="small" color="primary" aria-label="zoom-in" > <Tooltip title="Zoom In"> <ZoomInSharpIcon /> </Tooltip> </Fab> <Fab id="collapse-all" size="small" color="primary" aria-label="collapse-all" tooltip="Collapse All" > <Tooltip title="Collapse All"> <FuseSvgIcon>material-outline:call_merge</FuseSvgIcon> </Tooltip> </Fab> </Stack> <> {Object.keys(props.treeData).length > 1 ? ( <Box t={2} ref={svgRef} className="chart-container" sx={{ flexGrow: 1 }} /> ) : ( <FuseLoading /> )} <PopupChart onClose={popupCloseHandler} show={visibility} data={popupData} title="" popupTabs={popupTabs} popupPosition={popupPosition} nodeName={nodeName} theme={theme} /> </> </> </div> </React.Fragment>
Modal.js
import { useEffect, useState, useRef } from "react"; import popupStyles from "./Popup.module.css"; import PropTypes from "prop-types"; import CircularProgress from "@mui/material/CircularProgress"; import * as am4core from "@amcharts/amcharts4/core"; import * as am4charts from "@amcharts/amcharts4/charts"; import Backdrop from "@mui/material/Backdrop"; import Modal from "@mui/material/Modal"; import Fade from "@mui/material/Fade"; import Typography from "@mui/material/Typography"; let margin = { top: 30, right: 50, bottom: 0, left: 100 }, height = 320 - margin.top - margin.bottom; const style = { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", width: 600, bgcolor: "background.paper", border: "2px solid #000", boxShadow: 24, borderRadius: "5%", p: 2, display: "flex", flexDirection: "column", }; return ( <div> <Modal aria-labelledby="transition-modal-title" aria-describedby="transition-modal-description" open={show} onClose={closeHandler} closeAfterTransition BackdropComponent={Backdrop} BackdropProps={{ timeout: 500, }} > <Fade in={show}> <Box sx={style}> <Typography variant="h6" sx={{ textAlign: "center" }} > {nodeName} </Typography> {props.data.length > 0 ? ( <Box sx={{ width: "100%" }}> <Tabs value={tabValue} onChange={(event, value) => setTabValue(value)} textColor="secondary" indicatorColor="secondary" aria-label="secondary tabs example" onClick={(e) => { console.log("Came here 01"); }} > {props.popupTabs.map((element, i) => ( <Tab key={element} onClick={(e) => { console.log("Came here"); setActiveTab(element); }} label={element} value={element} /> ))} </Tabs> { <Box id="popup_chart" sx={{ height: height + margin.top + margin.bottom + "px", alignItems: "center", }} className="svgWrapper" ></Box> } </Box> ) : ( <Typography variant="p" gutterBottom component="div" sx={{ textAlign: "center" }} > No Drills available for this node </Typography> )} {loading && ( <Box sx={{ display: "flex", flexGrow: 1, justifyContent: "center", alignItems: "center", position: "absolute", top: "50%", right: "50%", }} > <CircularProgress color="success" /> </Box> )} </Box> </Fade> </Modal> </div> );
当前效果:
解决方法
MUI Modal默认基于整个视口定位,要让它在指定容器内居中,需修改两个核心设置:
- 关联目标容器:给Modal指定父容器,让它挂载到目标div内部而非body下
- 调整定位样式:修改Modal和内容框的定位规则,使其相对于目标容器居中
步骤1:给目标容器添加Ref并传递给Modal组件
修改File.js,为check-mate容器创建ref,并将其传递给PopupChart组件:
import { useRef } from 'react'; // 在组件内部创建容器ref const containerRef = useRef(null); <React.Fragment> <div className="check-mate" ref={containerRef} // 绑定ref style={{ backgroundColor: 'red', position: 'relative', // 必须保留,作为绝对定位的基准容器 }} > {/* 其他内容不变 */} <PopupChart onClose={popupCloseHandler} show={visibility} data={popupData} title="" popupTabs={popupTabs} popupPosition={popupPosition} nodeName={nodeName} theme={theme} containerRef={containerRef} // 传递ref给Modal组件 /> </div> </React.Fragment>
步骤2:修改Modal组件的容器关联与样式
更新Modal.js,接收传入的ref,指定Modal的父容器,并调整定位样式:
// 接收props中的containerRef const PopupChart = ({ containerRef, show, closeHandler, ...props }) => { let margin = { top: 30, right: 50, bottom: 0, left: 100 }, height = 320 - margin.top - margin.bottom; const style = { position: "absolute", top: "50%", left: "50%", transform: "translate(-50%, -50%)", width: 600, bgcolor: "background.paper", border: "2px solid #000", boxShadow: 24, borderRadius: "5%", p: 2, display: "flex", flexDirection: "column", }; return ( <div> <Modal container={containerRef.current} // 指定Modal的父容器 aria-labelledby="transition-modal-title" aria-describedby="transition-modal-description" open={show} onClose={closeHandler} closeAfterTransition BackdropComponent={Backdrop} BackdropProps={{ timeout: 500, }} // 让Modal占满整个目标容器 sx={{ position: 'absolute', top: 0, left: 0, width: '100%', height: '100%', }} > <Fade in={show}> <Box sx={style}> {/* Modal内部内容保持不变 */} </Box> </Fade> </Modal> </div> ); }; // 添加propTypes校验 PopupChart.propTypes = { containerRef: PropTypes.object.isRequired, // 其他propTypes... }; export default PopupChart;
关键说明
- 目标容器
check-mate必须设置position: relative,否则内部绝对定位元素会向上寻找第一个定位父元素 - Modal的
container属性会将Modal的根节点挂载到指定容器内部,替代默认的body挂载 - 给Modal添加的
sx样式让它占满目标容器,确保遮罩层(Backdrop)覆盖整个容器而非屏幕 - 内容框的
top:50% + left:50% + transform: translate(-50%,-50%)是标准的绝对定位居中方案
内容的提问来源于stack exchange,提问作者Jamal Butt
相关产品推荐
相关产品推荐

