它不起作用的原因是因为百分比高度的过渡并不是你所期望的。
CSS 中的百分比高度和宽度指的是其父级的高度和宽度,而不是他们自己的高度。
MDN Percentage
CSS 数据类型表示百分比值。它是
通常用于定义相对于元素的父对象的大小。
许多属性都可以使用百分比,例如width、height、
margin、padding 和 font-size。
使用百分比高度/宽度转换相同元素的示例。一个有一个高度/宽度为 50 像素的容器元素,另一个没有。
div.container {
width: 50px;
height: 50px;
}
div.transition {
width: 100%;
height: 100%;
background: red;
transition: width 2s, height 4s;
}
div.transition:hover {
width: 300%;
height: 500%;
}
Transition div with a 50px container
<div class="container">
<div class="transition">
<p>test</p>
</div>
</div>
Transition div without a container:
<div class="transition">
<p>test</p>
</div>
我们真正想要的是从 0px 过渡到自动高度。不幸的是,浏览器不支持自动高度转换。
Using CSS Transitions Auto Dimensions 中有一篇很好的文章,其中包括一些获得您想要的东西及其缺点的方法。
为什么这个问题没有在浏览器级别解决?
根据 Mozilla 开发者网络文档,自动值已
有意从 CSS 过渡规范中排除。看起来像
有几个人要求过,但仔细想想,
至少有点感觉它没有被包括在内。这
重新计算所有的大小和位置的浏览器进程
元素基于它们的内容以及它们与每个元素的交互方式
其他(称为“回流”)是昂贵的。如果你要转换一个
元素进入自动高度,浏览器必须执行
对该动画的每个阶段进行回流,以确定所有
其他元素应该移动。这不能被缓存或计算在
简单的方法,因为它不知道开始和/或结束值
直到过渡发生的那一刻。这将显着
使必须在幕后完成的数学复杂化,并且可能
以一种可能不明显的方式降低性能
开发者。
How can I transition height: 0; to height: auto; using CSS? 也有一些很好的解决方法,尽管这确实没有灵丹妙药。
这绝对是一个众所周知的问题,并且有一个 request 用于规范更改以允许自动转换,但我认为它还没有消失。
至于对你在 React 过渡组中工作的过渡类型的支持:
Slide Down Animation 和Trying to fade out element then slide up 总体上都有相同的答案:指出React Bootstrap's Collapse component 是如何做到的。
您需要依靠找到 dom 节点的实际高度并将其用作过渡的一部分:
getDimension() {
return typeof this.props.dimension === 'function'
? this.props.dimension()
: this.props.dimension;
}
// for testing
_getScrollDimensionValue(elem, dimension) {
return `${elem[`scroll${capitalize(dimension)}`]}px`;
}
/* -- Expanding -- */
handleEnter = (elem) => {
elem.style[this.getDimension()] = '0';
}
handleEntering = (elem) => {
const dimension = this.getDimension();
elem.style[dimension] = this._getScrollDimensionValue(elem, dimension);
}
handleEntered = (elem) => {
elem.style[this.getDimension()] = null;
}
/* -- Collapsing -- */
handleExit = (elem) => {
const dimension = this.getDimension();
elem.style[dimension] = `${this.props.getDimensionValue(dimension, elem)}px`;
triggerBrowserReflow(elem);
}
handleExiting = (elem) => {
elem.style[this.getDimension()] = '0';
}
使用 Collapse 类中的功能的快速而肮脏的示例,用于使用功能较少的解决方案的代码工作示例(注意,主要基于上面链接的 Collapse.js 代码):
const { Transition } = ReactTransitionGroup;
const { EXITED, ENTERED, ENTERING, EXITING } = Transition;
const { useState } = React;
// Quick and dirty classNames functionality
const classNames = (...names) => names.filter((name) => name).join(' ');
const ButtonShowMore = ({ isOpen, click }) => {
return <button onClick={click}>{isOpen ? 'Close' : 'Open'}</button>;
};
// Heavily based on https://github.com/react-bootstrap/react-bootstrap/blob/next/src/Collapse.js#L150
// for the purpose of demonstration without just pulling in the module:
function triggerBrowserReflow(node) {
node.offsetHeight; // eslint-disable-line no-unused-expressions
}
const collapseStyles = {
[EXITED]: 'collapse',
[EXITING]: 'collapsing',
[ENTERING]: 'collapsing',
[ENTERED]: 'collapse in',
};
const Collapse = ({ children, ...props }) => {
const handleEnter = (elem) => (elem.style.height = '0');
const handleEntering = (elem) =>
(elem.style.height = `${elem.scrollHeight}px`);
const handleEntered = (elem) => (elem.style.height = null);
const handleExit = (elem) => {
elem.style.height = `${elem.scrollHeight}px`;
triggerBrowserReflow(elem);
};
const handleExiting = (elem) => (elem.style.height = '0');
return (
<Transition
{...props}
onEnter={handleEnter}
onEntering={handleEntering}
onEntered={handleEntered}
onExit={handleExit}
onExiting={handleExiting}
>
{(state, innerProps) =>
React.cloneElement(children, {
...innerProps,
className: classNames(
props.className,
children.props.className,
collapseStyles[state]
),
})
}
</Transition>
);
};
const Card = () => {
const [showMoreInfo, setShowMoreInfo] = useState(false);
return (
<div className="Card">
<ButtonShowMore
isOpen={showMoreInfo}
click={() => setShowMoreInfo(!showMoreInfo)}
/>
<Collapse in={showMoreInfo} className="Card-Details" timeout={1000}>
<div style={{ height: 0 }}>
<p>details</p>
<p>details</p>
</div>
</Collapse>
</div>
);
};
ReactDOM.render(<Card />, document.querySelector('#root'));
.collapsing {
-webkit-transition: height 1s ease;
-moz-transition: height 1s ease;
-o-transition: height 1s ease;
transition: height 1s ease;
overflow: hidden;
}
.collapse {
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-transition-group/4.4.1/react-transition-group.min.js"></script>
<div id="root" />