可变高度的Material UI Dialog如何固定锚点且保持居中不跳动
解决方案
可以通过CSS媒体查询+少量DOM逻辑实现需求,原理是仅在宽度≥600px的设备上,固定Dialog的顶部锚点为完整列表状态下的居中偏移值,内容高度变化时不再重新计算居中位置,实现代码如下:
- 首先定义样式:
const useStyles = makeStyles((theme) => ({ // 覆盖scrollPaper默认的居中对齐,仅在≥600px屏幕生效 pivottedDialog: { [theme.breakpoints.up('sm')]: { alignItems: 'flex-start', }, }, // 给paper容器加动态top属性 fixedTopPaper: { [theme.breakpoints.up('sm')]: { top: 'var(--dialog-top, auto)', } } }));
- 组件逻辑实现:
const MyDialog = () => { const classes = useStyles(); const paperRef = useRef(null); // 标记当前是否为未筛选的完整列表状态 const [isFullList, setIsFullList] = useState(true); // 替换为你自己的筛选值状态 const [filterValue, setFilterValue] = useState(''); // 计算完整列表状态下的居中top值并更新 const setDialogTop = useCallback(() => { if (window.innerWidth < 600 || !paperRef.current || !isFullList) return; // 先重置top让Dialog默认居中,拿到正确的偏移量 paperRef.current.style.removeProperty('top'); const rect = paperRef.current.getBoundingClientRect(); paperRef.current.style.setProperty('--dialog-top', `${rect.top}px`); }, [isFullList]); // 首次挂载、窗口resize时更新top值 useEffect(() => { setDialogTop(); window.addEventListener('resize', setDialogTop); return () => window.removeEventListener('resize', setDialogTop); }, [setDialogTop]); // 筛选状态变化时同步更新 useEffect(() => { // 筛选值为空时判定为完整列表状态,可根据你的业务逻辑修改判定条件 if (filterValue === '') { setIsFullList(true); // 等待DOM更新完成后重新计算top setTimeout(setDialogTop, 0); } else { setIsFullList(false); } }, [filterValue, setDialogTop]); return ( <Dialog {...otherProps} classes={{ scrollPaper: classes.pivottedDialog, paper: classes.fixedTopPaper }} PaperProps={{ ref: paperRef }} > {/* 你的筛选组件、列表内容 */} </Dialog> ) }
实现说明
- 小屏设备(宽度<600px)保持MUI Dialog默认的居中行为,不影响移动端体验
- 仅当列表处于未筛选的完整状态时,才会重新计算Dialog的居中top值,筛选导致列表高度变化时,top值固定不变,避免Dialog跳动
- 监听窗口resize事件,适配所有≥600px的屏幕尺寸,保证响应式效果
内容的提问来源于stack exchange,提问作者theJuls
相关产品推荐
相关产品推荐

