首页 > 解决方案 > 使用材质 UI 时动画未触发

问题描述

向我现有的应用程序添加材质 UI 时遇到问题。基本上发生的情况是,当我将材质 UI 组件添加到我的模态时,不会触发模态的进入动画。将材质 UI 降级到 1.0.0 或删除所有 MUI 组件可以解决此问题。此外,使用任何其他 UI 库都不会导致此问题。

https://codesandbox.io/s/mui-animation-issue-mgph3

import React, { useState, useEffect } from "react";
import styled from "styled-components";
import Test from "./Test";

const Overlay = styled.div`
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
  z-index: 10000;
`;

const Modal = styled.div`
  position: absolute;
  display: flex;
  flex-direction: column;
  justify-content: center;
  width: 100px;
  height: 100px;
  align-items: center;
  background: white;
`;

const modalsComponentLookupTable = {
  Test
};

const ModalContainer = ({ setModals, children }) => {
  const [modalStyle, setModalStyle] = useState({
    opacity: 0,
    transition: "opacity 2000ms",
    willChange: "opacity",
    transitionTimingFunction: "cubic-bezier(0.165, 0.840, 0.440, 1.000)"
  });
  const [bgStyle, setBgStyle] = useState({
    background: "rgba(0, 0, 0, 1)",
    willChange: "opacity",
    transition: "opacity 2000ms cubic-bezier(0.165, 0.840, 0.440, 1.000)",
    opacity: 0
  });
  const unMountStyle = () => {
    // css for unmount animation
    setModalStyle({
      opacity: 0,
      transition: "opacity 2200ms",
      willChange: "opacity",
      transitionTimingFunction: "cubic-bezier(0.165, 0.840, 0.440, 1.000)"
    });
    setBgStyle({
      background: "rgba(0, 0, 0, 1)",
      willChange: "opacity",
      transition: "opacity 2200ms cubic-bezier(0.165, 0.840, 0.440, 1.000)",
      opacity: 0
    });
  };

  const mountStyle = () => {
    // css for mount animation
    setModalStyle({
      opacity: 1,
      transition: "opacity: 2000ms",
      willChange: "opacity",
      transitionTimingFunction: "cubic-bezier(0.165, 0.840, 0.440, 1.000)"
    });

    setBgStyle({
      willChange: "opacity",
      opacity: 1,
      background: "rgba(0, 0, 0, 1)",
      transition: "opacity 2000ms cubic-bezier(0.165, 0.840, 0.440, 1.000)"
    });
  };

  useEffect(() => {
    mountStyle();
  }, []);

  const back = e => {
    e.stopPropagation();
    unMountStyle();
    setTimeout(() => setModals([]), 2200);
  };

  return (
    <Overlay onClick={back} style={bgStyle}>
      <Modal style={modalStyle}>{children}</Modal>
    </Overlay>
  );
};

const ModalsManager = ({ modals, setModals }) => {
  const renderedModals = modals.map(modalDescription => {
    const ModalComponent = modalsComponentLookupTable[modalDescription];

    return (
      <ModalContainer setModals={setModals}>
        <ModalComponent />
      </ModalContainer>
    );
  });

  return <span>{renderedModals}</span>;
};

export default ModalsManager;

Test组件包含任何类型的 MUI 组件时,不会触发进入动画。控制台没有错误。显然,我的代码中确实触发了这个问题,因为我已经在他们的 github 上打开了一个问题,他们说这不是他们的库的问题:https ://github.com/mui-org/material-ui/issues/17888

标签: cssreactjsmaterial-ui

解决方案


我已经充分隔离了这一点,以确信这不是Material-UI 的问题,而是用于过渡动画的方法的脆弱性。

要打破它,我所要做的就是包含一个在安装时立即重新渲染的组件(在componentDidMount或中useLayoutEffect)。这是TransitionGroup 所做的事情,并且TransitionGroupTouchRipple使用,ButtonBase它被几个 Material-UI 组件(例如按钮)使用。

将您的Test组件更改为以下内容足以导致不良行为:

import React from "react";

const RerenderOnMount = () => {
  const [, setMyState] = React.useState(false);
  React.useLayoutEffect(() => {
    setMyState(true);
  }, []);
  return null;
};
const Test = () => {
  return (
    <div
      css={`
        height: 100px;
        width: 100px;
        z-index: 5;
      `}
      onClick={e => e.stopPropagation()}
    >
      <div>
        Test
        <RerenderOnMount />
      </div>
    </div>
  );
};

export default Test;

编辑 MUI 动画问题

在上面仍然重现您的问题的示例中,没有使用 Material-UI 组件。

我相信子元素的重新渲染会影响(延迟)浏览器首次尝试绘制模式的时间,并导致浏览器无法识别您调用时发生的转换mountStyle(即行为与您最初使用了“安装”样式,而不是识别从默认样式到安装样式的过渡)。在 React 中获得正确的时间以确保浏览器进行转换的技巧是人们通常使用react-transition-group来帮助解决这个问题的原因(就像 Material-UI 所做的那样)。

我可以通过在 中调用mountStylevia来使您的代码正常工作,但我不能保证此 hack 在其他情况下(取决于模式中的内容)或不同的浏览器不会出现问题,我会改为推荐重新设计转换以管理进入/退出状态。这是我的hack的工作版本: https ://codesandbox.io/s/mui-animation-issue-505rj 。setTimeoutuseEffectreact-transition-groupsetTimeout


推荐阅读