可折叠的内容面板,使用 max-height 或 CSS Grid 实现平滑的展开收起动画
点击面板标题展开或收起内容,体验两种不同的实现方式:
💡 提示:单开模式下,打开新面板会自动关闭其他面板;多开模式下可以同时展开多个面板
CSS Transform 是一个强大的 CSS 属性,允许你对元素进行 2D 或 3D 变换。
主要功能包括:
Transform 使用 GPU 加速,性能优于直接修改 top、left 等属性。
使用 Transform 有以下优势:
这使得 Transform 成为现代 Web 动画的首选方案。
Transform 在实际项目中有广泛应用:
使用 Transform 时的注意事项:
遵循这些原则可以创建高质量的用户体验。
使用 max-height 过渡是最常用的方法,兼容性好且效果自然,但是需要设置高度
<div class="accordion-item">
<div class="accordion-header">
<h3>标题</h3>
<span class="accordion-toggle">▼</span>
</div>
<div class="accordion-content">
<div class="accordion-body">
内容...
</div>
</div>
</div>
.accordion-content {
/* 初始状态:高度为 0 */
max-height: 0;
overflow: hidden;
transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
/* 展开状态:设置足够大的高度 */
.accordion-content.open {
max-height: 500px; /* 根据实际内容调整 */
}
/* 箭头旋转动画 */
.accordion-toggle {
transition: transform 0.3s ease;
}
.accordion-header.active .accordion-toggle {
transform: rotate(180deg);
}
使用 CSS Grid 的 grid-template-rows 实现,动画流畅且不占据文档流空间:
.accordion-content {
/* 使用 Grid 布局控制高度 */
display: grid;
grid-template-rows: 0fr; /* 折叠状态 */
transition: grid-template-rows 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.accordion-content.open {
grid-template-rows: 1fr; /* 展开状态 */
}
/* 内容需要设置 overflow: hidden */
.accordion-body {
overflow: hidden;
}
通过 JavaScript 动态计算内容的实际高度,实现最精确的过渡效果:
💡 什么是"强制重排"?
当你用 JavaScript 修改 CSS 属性时,浏览器为了性能优化,不会立即应用这些改变,而是会批量处理。 如果直接写
height = '0' 然后立即写 height = '300px',浏览器会合并这两次操作,直接显示
300px,没有过渡动画!
强制重排就是通过访问某些 DOM 属性(如
offsetHeight)来强制浏览器立即计算并应用样式改变。 这样浏览器就知道:从 0 过渡到
300px,会产生平滑的动画效果。
常用的触发重排的属性:offsetHeight、offsetWidth、scrollHeight、clientHeight、getBoundingClientRect()
等
.accordion-content {
height: 0;
overflow: hidden;
transition: height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
/* 展开状态的高度由 JS 动态设置 */
.accordion-content.open {
/* height 将被 JS 设置为 scrollHeight */
}
function expand(content) {
// 1. 设置初始高度为 0
content.style.height = '0';
content.style.overflow = 'hidden';
// 2. 强制重排,确保初始状态生效
// 访问 offsetHeight 会强制浏览器立即计算布局
// 如果不这样做,浏览器会合并步骤 1 和步骤 3,直接跳到最终状态,没有动画
content.offsetHeight;
// 3. 设置目标高度为实际内容高度
// 现在浏览器知道:从 0 过渡到 scrollHeight,会产生动画
const targetHeight = content.scrollHeight;
content.style.height = targetHeight + 'px';
// 4. 动画结束后设置为 auto,允许内容自适应
content.addEventListener('transitionend', function handler() {
if (content.classList.contains('open')) {
content.style.height = 'auto';
}
content.style.overflow = '';
content.removeEventListener('transitionend', handler);
});
}
function collapse(content) {
// 1. 先设置为具体高度值(从 auto 过渡需要先固定高度)
const currentHeight = content.scrollHeight;
content.style.height = currentHeight + 'px';
content.style.overflow = 'hidden';
// 2. 强制重排
content.offsetHeight;
// 3. 设置目标高度为 0
content.style.height = '0';
}
实现单开和多开两种模式:
let multipleMode = false; // 单开/多开模式
const headers = document.querySelectorAll('.accordion-header');
headers.forEach(header => {
header.addEventListener('click', () => {
const item = header.parentElement;
const content = item.querySelector('.accordion-content');
const isOpen = content.classList.contains('open');
// 单开模式:关闭其他面板
if (!multipleMode && !isOpen) {
document.querySelectorAll('.accordion-content').forEach(c => {
c.classList.remove('open');
c.parentElement.querySelector('.accordion-header')
.classList.remove('active');
});
}
// 切换当前面板
content.classList.toggle('open');
header.classList.toggle('active');
});
});
offsetHeight 等属性会强制浏览器立即计算布局, 确保初始状态(height:
0)生效后再应用目标状态(height: 300px),从而产生平滑的过渡动画
height = '0'; height = '300px';,浏览器会合并操作, 直接显示 300px,看不到动画效果