当 height: auto
时,设置 transition
是无法触发动画的,CSS 需要知道具体的 height 值,因此引申其他解决方案:
- 方案一:使用
transform
的scaleY
,去放大缩小。- 缺点
- 内容会变形
- 会占据原来的内容尺寸
- 缺点
- 方案二:设置
max-height
- 缺点
- 随着内容的增加,需要重新考虑设置
max-height
的值 transition-timing
是作用在max-height
上的
- 随着内容的增加,需要重新考虑设置
- 缺点
- 方案三:获取元素的
height
,重新设置。- 缺点
- 非纯 CSS
- 缺点
可以看到,每种方案不优雅,矮个子里拔高。接下来来拔一下方案三,我们将以 React 为例子:
- 创建一个组件,使用按钮去展开收缩
<div className={styles.wrapper}>
<Button className={styles.button} onClick={collapseChange}>
{isExpand ? "Collapse" : "Expand"}
</Button>
<div
className={clsx(styles.content, isExpand && styles.expanded)}
style={{
height: height + "px",
}}Ï
>
<div ref={ref}>{children}</div>
</div>
</div>
- 创建
ref
,引用元素,当用户点击展开按钮时,获取元素的高度height
const ref = useRef<HTMLDivElement>(null);
const [height, setHeight] = useState<number>(0);
const [isExpand, setIsExpanded] = useState(defaultIsExpanded);
const collapseChange = () => {
if (isExpand) {
setHeight(0);
} else {
setHeight(ref.current?.clientHeight ?? 0);
}
setIsExpanded(!isExpand);
};
这里可以缓存元素的高度,但是这里业务场景中,子元素的高度可能会动态变化,因此不做缓存。
- 设置 CSS,当元素高度变化时,触发
transition
动画
.content {
grid-column: span 2;
transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;
transition-duration: 242ms;
overflow-y: hidden;
}