点击MUI Dialog外部时抖动效果不生效的原因排查
问题分析与解决方案
你的代码存在几个关键问题,导致点击Dialog外部时没有触发抖动效果:
1. Keyframes 使用方式错误
MUI/Emotion 的 keyframes 返回的是动画名称对象,并非字符串,直接拼到 style.animation 里浏览器无法识别,因为它不是有效的CSS动画名称。
2. DOM 元素获取与动画目标错误
你给 Dialog 组件添加了 id="custom-dialog",但弹窗的可视主体是内部的 Paper 组件,Dialog 本身只是外层容器。同时,用 document.getElementById 不是React推荐的DOM操作方式,应该用 useRef 来精准获取目标元素。
3. 未禁用外部点击关闭行为
默认情况下,点击Dialog外部会触发弹窗关闭,你需要设置 disableOutsideClick={true} 阻止这个默认行为,否则动画可能被关闭动作打断。
修正后的代码
import { useState, useRef } from 'react'; import { Dialog, Backdrop, Paper, Box, Typography, Button, DialogTitle, DialogContent, DialogContentText, DialogActions, keyframes } from '@mui/material'; import CrossIcon from './CrossIcon'; // 确保图标路径正确 export const StatusModalFail = (): React.ReactElement => { // 定义抖动动画 const shakeAnimation = keyframes` 10%, 90% { transform: translate3d(-1px, 0, 0); } 20%, 80% { transform: translate3d(2px, 0, 0); } 30%, 50%, 70% { transform: translate3d(-4px, 0, 0); } 40%, 60% { transform: translate3d(4px, 0, 0); } `; const [open, setOpen] = useState(false); const [isShaking, setIsShaking] = useState(false); const paperRef = useRef<Paper>(null); // 获取Paper组件的引用 const CustomBackdrop = (props: any) => { const handleClick = () => { // 触发抖动状态 setIsShaking(true); // 500ms后重置抖动状态 setTimeout(() => setIsShaking(false), 500); }; return <Backdrop {...props} onClick={handleClick} />; }; const handleCloseButton = () => { setOpen(false); }; // 可添加打开弹窗的方法,供外部调用或内部触发 // const handleOpen = () => setOpen(true); return ( <Dialog aria-labelledby="alert-dialog-title" aria-describedby="alert-dialog-description" open={open} disableEscapeKeyDown disableOutsideClick={true} // 禁止点击外部关闭弹窗 PaperProps={{ ref: paperRef, sx: { width: '100%', maxWidth: '500px', height: '100%', maxHeight: '250px', // 根据状态动态应用动画 animation: isShaking ? `${shakeAnimation} 0.5s linear` : 'none', }, }} BackdropComponent={CustomBackdrop} > <Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}> <CrossIcon style={{ color: '#fa6757', width: 48, height: 48, padding: 8, }} /> </Box> <DialogTitle id="alert-dialog-title" sx={{ justifyContent: 'center', alignItems: 'center', paddingTop: '2px', textAlign: 'center', }} > <Typography variant="buttons.default">保存失败</Typography> </DialogTitle> <DialogContent sx={{ textAlign: 'center', paddingTop: '2px' }}> <DialogContentText id="alert-dialog-description"> <Typography color="textSecondary" variant="body.default"> 请稍后重试 </Typography> <DialogActions sx={{ justifyContent: 'center', marginTop: 4 }}> <Button variant="outlined" sx={{ width: 350, height: 50, fontSize: 16 }} onClick={handleCloseButton} > 关闭 </Button> </DialogActions> </DialogContentText> </DialogContent> </Dialog> ); };
关键修改说明
- 用
useRef获取Paper组件引用,确保动画作用在弹窗的可视主体上。 - 用状态
isShaking控制动画的开启和关闭,遵循React状态驱动的开发理念,避免直接操作DOM。 - 设置
disableOutsideClick={true}阻止点击外部关闭弹窗的默认行为,确保动画完整执行。 - 通过
PaperProps.sx.animation动态应用动画,利用MUI的sx属性直接使用keyframes定义的动画。
内容的提问来源于stack exchange,提问作者mariadev
相关产品推荐
相关产品推荐

