Transitions on the CSS display property

Transitions on the CSS display property

技术背景

在前端开发中,我们经常需要对元素的显示和隐藏进行动画过渡效果的处理。然而,CSS 的 display 属性(如 display: nonedisplay: block)本身并不支持过渡效果。当我们直接切换 display 属性时,元素会瞬间显示或隐藏,无法实现平滑的过渡动画。这是因为 display 属性决定了元素的渲染模式,不同的渲染模式(如块级元素、内联元素等)之间的转换很难通过平滑的动画来实现。为了解决这个问题,开发者们探索了多种替代方案。

实现步骤

1. 使用 visibilityopacity 组合

通过结合 visibilityopacity 属性,可以实现元素的淡入淡出效果。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
div {
border: 1px solid #eee;
}
div > ul {
visibility: hidden;
opacity: 0;
transition: visibility 0s, opacity 0.5s linear;
}
div:hover > ul {
visibility: visible;
opacity: 1;
}
1
2
3
4
5
6
7
<div>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>

在这个示例中,当鼠标悬停在 div 元素上时,ul 元素会逐渐淡入显示。

2. 使用 CSS 动画

可以通过定义 @keyframes 来创建自定义的动画效果。示例代码如下:

1
2
3
4
5
6
7
8
9
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.parent:hover .child {
display: block;
-webkit-animation: fadeIn 1s;
animation: fadeIn 1s;
}
1
2
3
4
5
6
<div class="parent">
Parent
<div class="child">
Child
</div>
</div>

这里定义了一个 fadeIn 动画,当鼠标悬停在 parent 元素上时,child 元素会执行该动画,实现淡入效果。

3. 使用 transition-delay

通过设置 transition-delay 属性,可以控制元素的过渡时间。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
#selector {
overflow: hidden;
height: 0;
opacity: 0;
transition: height 0ms 400ms, opacity 400ms 0ms;
}
#selector.visible {
height: 100px;
opacity: 1;
transition: height 0ms 0ms, opacity 400ms 0ms;
}

当添加 visible 类时,heightopacity 会立即开始动画;当移除 visible 类时,opacity 先开始动画,height 等待 400ms 后恢复初始值。

4. 使用 transition-behavior: allow-discrete

在 Chrome 117 及以上版本中,可以使用 transition-behavior: allow-discrete 来实现 display 属性的过渡。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
div:nth-child(2) {
@starting-style {
opacity: 0;
}
transition-property: display, opacity;
transition-duration: 2s;
transition-behavior: allow-discrete;
}
body:hover > div:nth-child(2) {
display: none;
opacity: 0;
}
1
2
3
<div>1</div>
<div>2</div>
<div>3</div>

这里的 transition-behavior: allow-discrete 允许 display 属性在过渡过程中进行离散的切换,从而实现平滑的过渡效果。

核心代码

使用 visibilityopacity 实现淡入淡出

1
2
3
4
5
6
7
8
9
div {
visibility: hidden;
opacity: 0;
transition: visibility 0s, opacity 0.5s linear;
}
div.active {
visibility: visible;
opacity: 1;
}

使用 CSS 动画实现淡入

1
2
3
4
5
6
7
8
9
10
11
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.element {
display: none;
}
.element.show {
display: block;
animation: fadeIn 1s;
}

使用 transition-delay 控制过渡时间

1
2
3
4
5
6
7
8
9
10
11
#element {
overflow: hidden;
height: 0;
opacity: 0;
transition: height 0ms 400ms, opacity 400ms 0ms;
}
#element.visible {
height: 100px;
opacity: 1;
transition: height 0ms 0ms, opacity 400ms 0ms;
}

使用 transition-behavior: allow-discrete 实现 display 过渡

1
2
3
4
5
6
7
8
9
10
11
12
.element {
@starting-style {
opacity: 0;
}
transition-property: display, opacity;
transition-duration: 2s;
transition-behavior: allow-discrete;
}
.element.hide {
display: none;
opacity: 0;
}

最佳实践

  • 避免直接切换 display 属性:尽量使用 visibilityopacityheight 等可过渡的属性来替代 display 属性的切换。
  • 使用 CSS 动画:对于复杂的过渡效果,使用 @keyframes 定义动画可以更好地控制动画的细节。
  • 结合 JavaScript:在某些情况下,结合 JavaScript 可以更灵活地控制元素的显示和隐藏,以及过渡效果的触发。

常见问题

1. 为什么 display 属性不支持过渡效果?

display 属性决定了元素的渲染模式,不同的渲染模式(如块级元素、内联元素等)之间的转换很难通过平滑的动画来实现。因此,浏览器默认禁用了 display 属性的过渡效果。

2. 使用 visibility 隐藏元素后,元素仍然占据空间怎么办?

可以结合 position: absolutez-index: -1 来使隐藏的元素不占据空间,同时使用 pointer-events: none 来防止元素响应鼠标事件。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.hidden-element {
position: absolute;
z-index: -1;
pointer-events: none;
visibility: hidden;
opacity: 0;
transition: visibility 0s, opacity .5s ease-out;
}
.hidden-element.visible {
position: static;
z-index: auto;
pointer-events: auto;
visibility: visible;
opacity: 1;
}

3. 如何实现元素的展开和收缩效果?

可以使用 max-height 属性来实现元素的展开和收缩效果。示例代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#panel {
background-color: red;
opacity: 0;
max-height: 0;
overflow: hidden;
visibility: hidden;
width: 100%;
transition: max-height 1s, visibility 1s, opacity 1s ease;
}
#panel.opened {
opacity: 1;
max-height: 500000px;
overflow: auto;
visibility: visible;
}
1
2
<button id="paneltrigger" aria-expanded="false">SHOW / HIDE</button>
<div id="panel">CONTENT</div>
1
2
3
4
5
6
7
8
9
10
11
12
const paneltrigger = document.querySelector("#paneltrigger");
const panel = document.querySelector("#panel");
paneltrigger.addEventListener('click', () => {
const isOpen = paneltrigger.getAttribute("aria-expanded") === "false" ? false : true;
if (isOpen) {
paneltrigger.setAttribute("aria-expanded", "false");
panel.classList.remove('opened');
} else {
paneltrigger.setAttribute("aria-expanded", "true");
panel.classList.add('opened');
}
});

Transitions on the CSS display property
https://119291.xyz/posts/transitions-on-the-css-display-property/
作者
ww
发布于
2025年5月23日
许可协议