React/Joy-UI折叠列表动画问题:cubic-bezier修复退出却破坏进入
React/Joy-UI折叠列表动画:进出动画异常解决
我正在尝试用React/Joy-UI实现折叠列表动画,当前Transition组件代码如下:
<Transition nodeRef={nodeRef} in={browseOpen} timeout={1000}> {(state: string) => ( <List aria-labelledby="nav-list-browse" sx={{ '& .JoyListItemButton-root': { p: '8px' }, transition: '1000ms', transitionProperty: 'max-height', overflow: 'hidden', ...{ exiting: { maxHeight: '0px'}, exited: { maxHeight: '0px'}, entering: { maxHeight: '500px'}, entered: { maxHeight: '500px'}, }[state], }} > {/* 列表项内容 */} </List> )} </Transition>
问题现象
- 初始设置下:列表展开正常,但折叠时存在明显延迟
- 添加
cubic-bezier(0, 1, 0, 1)到transition属性后:折叠动画恢复正常,但展开动画完全失效
添加cubic-bezier前的效果:
添加cubic-bezier后的效果:
问题根源
用固定值max-height: 500px做动画的核心问题:
- 折叠延迟:列表实际高度远小于500px,动画会先消耗大量时间在「从500px降到实际高度」的无视觉变化阶段,最后才快速降到0,导致看起来延迟。
- 展开失效:
cubic-bezier(0,1,0,1)是先极慢后突变的曲线,展开时动画大部分时间都在「从0到实际高度」之前的阶段,视觉上完全看不到变化。
解决办法
方案1:动态计算实际高度(推荐)
直接获取列表展开后的真实高度,用这个值作为动画的目标max-height,彻底避免无效的高度区间:
import { useRef, useState, useEffect } from 'react'; import { Transition } from 'react-transition-group'; import { List } from '@mui/joy'; // 组件内部代码 const listRef = useRef(null); const [listRealHeight, setListRealHeight] = useState('0px'); // 组件挂载时获取列表实际高度 useEffect(() => { if (listRef.current) { setListRealHeight(`${listRef.current.scrollHeight}px`); } }, []); // 渲染部分 <Transition nodeRef={listRef} in={browseOpen} timeout={1000}> {(state) => ( <List ref={listRef} aria-labelledby="nav-list-browse" sx={{ '& .JoyListItemButton-root': { p: '8px' }, transition: 'max-height 1000ms ease', overflow: 'hidden', maxHeight: { exiting: '0px', exited: '0px', entering: listRealHeight, entered: listRealHeight, }[state], }} > {/* 列表项内容 */} </List> )} </Transition>
方案2:针对状态设置不同过渡曲线
如果不想动态计算高度,可以给进入、退出阶段分别配置合适的曲线:
<Transition nodeRef={nodeRef} in={browseOpen} timeout={1000}> {(state) => ( <List aria-labelledby="nav-list-browse" sx={{ '& .JoyListItemButton-root': { p: '8px' }, overflow: 'hidden', maxHeight: { exiting: '0px', exited: '0px', entering: '500px', entered: '500px', }[state], // 进入用常规ease曲线,退出用你需要的贝塞尔曲线 transition: state.includes('enter') ? 'max-height 1000ms ease' : 'max-height 1000ms cubic-bezier(0, 1, 0, 1)', }} > {/* 列表项内容 */} </List> )} </Transition>
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

