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

如何在React/MUI 5中实现组件宽度的平滑变化效果?

解决方案

核心原因

你给width加transition不生效的根本原因是:默认状态下div的width为auto,CSS transition不支持auto和具体数值之间的过渡动画,只有两个具体数值之间的变化才会触发过渡效果。

方案1:max-width hack(最简单实现)

这是最常用的轻量实现方案,用max-width替代width做过渡:

  • 给div设置一个明显大于你预期最长文本宽度的max-width作为展开态的值,收起态设为0
  • 给max-width设置transition属性即可

代码示例(MUI 5 styled写法):

import { styled } from '@mui/material/styles';

const AnimatedTextContainer = styled('div')(({ theme }) => ({
  border: '1px solid #2196f3', // 替换为你的自定义边框样式
  padding: theme.spacing(1, 2),
  whiteSpace: 'nowrap', // 禁止文本换行,避免高度变化
  overflow: 'hidden',
  maxWidth: 0,
  transition: 'max-width 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
  '&.text-not-empty': {
    maxWidth: '300px', // 该值需要大于所有可能出现的文本的最大宽度
  }
}));

// 组件内使用
import { useState } from 'react';

export default function MyComponent() {
  const [text, setText] = useState('');
  const hasText = Boolean(text.trim());

  return (
    <AnimatedTextContainer className={hasText ? 'text-not-empty' : ''}>
      {text}
    </AnimatedTextContainer>
  );
}

注意:如果设置的maxWidth远大于实际文本宽度,会出现过渡速度比预期快的情况,按需调整取值即可。

方案2:精确宽度计算(效果最优)

如果需要完全贴合文本宽度的平滑过渡,可以预先计算文本的实际渲染宽度,给div设置具体的width数值,触发transition:

import { useState, useRef, useEffect } from 'react';
import { Box } from '@mui/material';

export default function MyComponent() {
  const [text, setText] = useState('');
  const [containerWidth, setContainerWidth] = useState(0);
  // 隐藏的测量节点,和目标容器共用所有影响宽度的样式
  const measureNode = useRef(document.createElement('span')).current;

  // 初始化测量节点
  useEffect(() => {
    Object.assign(measureNode.style, {
      position: 'absolute',
      visibility: 'hidden',
      pointerEvents: 'none',
      whiteSpace: 'nowrap',
      padding: '8px 16px', // 和目标容器的padding一致
      border: '1px solid #2196f3', // 和目标容器的border一致
      fontSize: '14px', // 和目标容器的fontSize一致
    });
    document.body.appendChild(measureNode);
    return () => document.body.removeChild(measureNode);
  }, [measureNode]);

  // 文本变化时更新宽度
  useEffect(() => {
    measureNode.textContent = text;
    setContainerWidth(measureNode.offsetWidth);
  }, [text, measureNode]);

  return (
    <Box
      sx={{
        border: '1px solid #2196f3',
        padding: 1,
        whiteSpace: 'nowrap',
        width: `${containerWidth}px`,
        transition: 'width 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
        overflow: 'hidden',
      }}
    >
      {text}
    </Box>
  );
}

该方案的优势是过渡效果完全匹配文本实际宽度,不会出现max-width方案的动画速度不均问题,适合文本长度变化频繁的场景。

内容的提问来源于stack exchange,提问作者radinvafaei

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 10:36:07