问题描述
下面是一个常见的效果,当鼠标悬浮时展开显示内容,离开时自动收回。
html
<div id="wrapper">
<h4>hover me!</h4>
<div id="content">
<p>Hello, World!</p>
<p>Hello, World!</p>
<p>Hello, World!</p>
<p>Hello, World!</p>
</div>
</div>
css
#wrapper {
width: 240px;
padding: 16px 24px;
border-radius: 12px;
}
#content p {
line-height: 32px;
}
#content {
height: 0;
overflow: hidden;
/*不起作用*/
transition: height 500ms;
}
#wrapper:hover #content {
height: auto;
}
由于通常不知道content
中具体内容所占的高度,因此常用height: auto;
设置高度。上面的示例可以良好的实现要求,问题出现在当我们需要为这个高度的变化添加过渡动画时,会发现transition: height 200ms;
不起作用,这是因为auto
并不是一个具体的高度数值。
使用 max-height 解决
一个常见方案是使用max-height
控制。这样确实可以实现过渡动画效果,但并不是一个"完美"的动画:
当过高的估计了content
容器的最大可能高度时,鼠标移入时的动画会的变快,而移出时动画会有延迟。这是因为有一部分时间花在了max-height
数值在content
容器的真实高度和设定值之间的过渡上,而这部分过渡时间不会影响页面元素的样式。
css
#wrapper {
width: 240px;
padding: 16px 24px;
border-radius: 12px;
}
#content p {
line-height: 32px;
}
#content {
/*起始高度*/
max-height: 0;
overflow: hidden;
transition: max-height 500ms;
}
#wrapper:hover #content {
/*确保大于content最大可能高度*/
max-height: 600px;
}
更好的方案:使用 grid
一个更完美的解决方案是使用网格布局:
注意在html
结构中,要为content
再添加一层子元素div,display: grid;
和grid-template-rows
属性仍然设置给content
,但此时overflow: hidden;
要设置给content
的子元素。
html
<div id="wrapper">
<h4>hover me!</h4>
<div id="content">
<div>
<p>Hello, World!</p>
<p>Hello, World!</p>
<p>Hello, World!</p>
<p>Hello, World!</p>
</div>
</div>
</div>
css
#wrapper {
width: 240px;
padding: 16px 24px;
border-radius: 12px;
}
#content p {
line-height: 32px;
}
#content {
display: grid;
grid-template-rows: 0fr;
transition:
grid-template-rows 500ms;
}
#content > div {
/*使子元素动画与父元素保持一致*/
grid-row: 1 / span 2;
/*overflow也设置给子元素*/
overflow: hidden;
}
#wrapper:hover #content {
grid-template-rows: 1fr;
}
讨论
grid-row: 1 / span 2;
这行代码看起来有些费解,因为毕竟只有一行子元素。它的作用是使子元素动画与父元素保持一致。
我们把动画放慢,并且给content
带上灰色背景,给content > div
带上红色背景,观察一下使用和不使用这个属性的区别:
注:这个问题是否发生似乎与浏览器的行为有关:在我测试时,使用Google Chrome和Microsoft Edge时二者表现是不同的,但在Firefox中是相同的,即使不写grid-row: 1 / span 2;
也不会引起这个问题。