You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

点击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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 17:59:57