如何在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
相关产品推荐
相关产品推荐

