如何在@react-spring useTransition中实现height从0到'auto'的过渡?
解决useTransition动态高度过渡动画问题
当使用useTransition时直接设置height: 'auto'会丢失动画效果,因为CSS过渡不支持从具体数值到auto的平滑过渡。以下是几种可行的解决方案:
方法一:通过ref获取实际高度过渡(推荐)
利用ref获取元素的真实内容高度,先过渡到该具体数值,动画结束后再设置为auto,既保留动画又支持动态高度自适应。
import { useTransition, animated } from 'react-spring'; import { useRef } from 'react'; function AnimatedDynamicList({ items }) { const transitions = useTransition(items, { keys: item => item.id, from: { height: 0, opacity: 0 }, enter: async (_, state) => { // 先让元素可见,确保能获取内容高度 await state.set({ opacity: 1 }); const realHeight = state.ref.current.scrollHeight; // 过渡到真实高度 await state.set({ height: realHeight }); // 动画结束后恢复auto,支持后续内容动态变化 state.set({ height: 'auto' }); }, leave: { height: 0, opacity: 0 }, config: { duration: 300 }, // 绑定ref到每个过渡元素 ref: ref => ref ? (state.ref = ref) : null, }); return ( <div className="list-container"> {transitions((style, item, state) => ( <animated.div ref={state.ref} style={{ ...style, overflow: 'hidden', margin: '8px 0', padding: '12px', border: '1px solid #eee', }} > {/* 动态高度的子内容 */} <h3>{item.title}</h3> <p>{item.description}</p> </animated.div> ))} </div> ); }
原理说明
- 先通过
opacity: 1让元素渲染内容,此时能通过scrollHeight获取到内容的真实高度; - 过渡到该真实高度数值,实现平滑动画;
- 动画完成后设置
height: auto,确保后续内容变化时容器能自动适应高度。
方法二:使用max-height替代height
如果场景中内容高度变化范围不大,可以用max-height代替height,设置一个足够大的目标值(只要超过内容可能的最大高度即可)。
import { useTransition, animated } from 'react-spring'; function SimpleAnimatedList({ items }) { const transitions = useTransition(items, { keys: item => item.id, from: { maxHeight: 0, opacity: 0 }, enter: { maxHeight: '2000px', opacity: 1 }, leave: { maxHeight: 0, opacity: 0 }, config: { duration: 300 }, }); return ( <div className="list-container"> {transitions((style, item) => ( <animated.div style={{ ...style, overflow: 'hidden', margin: '8px 0', padding: '12px', border: '1px solid #eee', }} > <h3>{item.title}</h3> <p>{item.description}</p> </animated.div> ))} </div> ); }
优缺点
- 优点:实现简单,无需ref;
- 缺点:若内容高度差异过大,动画后期会出现速度变快的不自然效果(因为
max-height从实际高度到设定的最大值这段是无效过渡)。
方法三:监听内容变化重新计算高度
当子元素内容动态更新时,通过useLayoutEffect重新获取高度并触发过渡,确保内容变化时也能有平滑动画。
import { useTransition, animated } from 'react-spring'; import { useRef, useState, useLayoutEffect } from 'react'; function AnimatedUpdatableItem({ children }) { const ref = useRef(null); const [targetHeight, setTargetHeight] = useState(0); const [isVisible, setIsVisible] = useState(true); useLayoutEffect(() => { if (ref.current && isVisible) { const height = ref.current.scrollHeight; setTargetHeight(height); // 动画结束后恢复auto const timer = setTimeout(() => setTargetHeight('auto'), 300); return () => clearTimeout(timer); } }, [children, isVisible]); const transitions = useTransition(isVisible, { from: { height: 0, opacity: 0 }, enter: { height: targetHeight, opacity: 1 }, leave: { height: 0, opacity: 0 }, config: { duration: 300 }, }); return ( <div> <button onClick={() => setIsVisible(!isVisible)}> {isVisible ? '收起' : '展开'} </button> {transitions((style) => ( <animated.div ref={ref} style={{ ...style, overflow: 'hidden', margin: '8px 0', padding: '12px', border: '1px solid #eee', }} > {children} </animated.div> ))} </div> ); }
内容的提问来源于stack exchange,提问作者alex
相关产品推荐
相关产品推荐

