手风琴动画(grid 折叠平滑展开动效)
手风琴动画组件:用 grid-template-rows 0fr 到 1fr 实现高度未知内容的平滑展开收起,独占式展开、箭头旋转、内容淡入,原生 button 无障碍,纯 CSS 加少量 JS。
效果演示(真实可交互)
点击任意标题,对应面板以平滑的高度过渡展开,右侧箭头旋转 180 度,内容淡入;再点其他标题,当前面板收起、新的面板展开,同一时间只保留一项展开。展开动画完全跟随内容实际高度,无论文字多长都不会出现「空跑」或跳变,收起时同样顺滑。
把内容容器设为 display: grid,再将 grid-template-rows 从 0fr 过渡到 1fr,高度未知的内容也能平滑展开,不再依赖 max-height 估算法,动画时长恒定且精确。
max-height 方案需要给一个足够大的上限值,展开过程会有一段「空跑」拖慢节奏;grid 0fr 到 1fr 的方案完全由内容实际高度撑开,快慢一致、无跳变。
可以。在折叠容器上监听 transitionend 事件即可,注意区分展开与收起的方向,并配合 transition-property 过滤掉其他属性的过渡事件,避免误触发。
标题使用原生 button 元素,天然支持 Tab 聚焦与回车、空格触发;同时维护 aria-expanded 属性反映展开状态,屏幕阅读器可以准确播报当前状态。
点击标题展开 / 收起;同一时间只保留一项展开
完整代码
HTML(标题按钮 + 双层内容容器):
<div class="acc-item open">
<button class="acc-head" aria-expanded="true">标题文字</button>
<div class="acc-body">
<div class="acc-inner">内容文字,高度任意</div>
</div>
</div>
CSS(grid 折叠动画的核心三行):
.acc-body {
display: grid;
grid-template-rows: 0fr; /* 收起:行高为 0 */
transition: grid-template-rows .4s ease;
}
.acc-item.open .acc-body { grid-template-rows: 1fr; } /* 展开:撑满内容 */
.acc-inner { overflow: hidden; } /* 防止内容溢出 */
JS(独占式展开,约 12 行):
const items = document.querySelectorAll('.acc-item');
items.forEach((item) => {
item.querySelector('.acc-head').addEventListener('click', () => {
const willOpen = !item.classList.contains('open');
items.forEach((it) => {
it.classList.remove('open');
it.querySelector('.acc-head').setAttribute('aria-expanded', 'false');
});
if (willOpen) {
item.classList.add('open');
item.querySelector('.acc-head').setAttribute('aria-expanded', 'true');
}
});
});
为什么用 grid 而不是 max-height:传统方案需要猜一个足够大的 max-height 上限,内容少时动画会「空跑」,内容多时又被截断;而 grid-template-rows 的 0fr 到 1fr 过渡由浏览器直接按内容真实高度插值,时长恒定、精确贴合,是近年来最优雅的纯 CSS 折叠方案。内层 .acc-inner 负责裁切溢出,JS 只做「独占展开」与 aria 状态同步。
参数说明
| 参数 | 位置 | 说明 |
|---|---|---|
| 展开时长 | transition: ... .4s | 250-600ms,内容多可调长 |
| 缓动曲线 | transition 的 easing | ease 或 cubic-bezier 均可 |
| 箭头方向 | .acc-chev 的旋转角度 | 45 度收起 / 225 度展开 |
| 内容淡入 | .acc-inner p 的 acc-fade | 可选,配合展开更有层次 |
| 展开策略 | JS 的 open 逻辑 | 演示为独占式,可改多开 |
兼容性与性能
- grid-template-rows 过渡需要 Chrome 107+ / Firefox 66+ / Safari 16+,属较新的 CSS 能力;旧浏览器会直接展开/收起(无动画),功能不受影响
- 只过渡 grid-template-rows 一个属性,内容裁切由 overflow: hidden 承担,无重排风暴
- 标题使用原生 button,Tab 聚焦、回车与空格触发开箱即用;aria-expanded 随状态同步,屏幕阅读器播报准确
- 内容区域可自由放入图片、表格、表单等复杂结构,动画依旧平滑
常见问题
Q: 展开时内容一闪而过没有动画? 检查浏览器版本是否支持 grid-template-rows 过渡(Chrome 107+)。也可以确认 .acc-inner 是否设置了 overflow: hidden,没有它内容会在动画期间溢出。
Q: 想支持同时展开多个面板? 把 JS 里「先全部收起」的循环去掉,只切换当前项的状态即可;此时每个面板的动画互不干扰,grid 方案天然支持多开。
Q: transitionend 回调触发多次? transition-property 默认监听所有过渡属性。若同时过渡了多个属性,在回调里过滤 e.propertyName 为 grid-template-rows 的再处理,可避免重复执行。
Q: 能商用吗? 可以。本组件为粤喵自研原创实现,免费商用无需署名。
许可与来源
- 许可证:自研原创 —— 粤喵自研原创,可免费用于个人与商业项目,无需署名。查看来源公示与许可清单
- 来源:粤喵团队原创自研(从效果思路到代码实现均为独立完成,未直接复制第三方代码)
- 首次发布:2026-09-01 · 最近更新:2026-09-01
- 修改日志:2026-09-01 首次发布;2026-09-13 补充许可证与来源标注、复制按钮说明与法务页入口
- 纠错:联系页面 / min@cactus.asia,2 个工作日内核实处理
常见问题
这个组件可以商用吗?
可以。本组件为粤喵自研原创,可免费用于个人和商业项目,无需署名。
浏览器兼容性如何?
支持所有现代浏览器(Chrome / Firefox / Safari / Edge 最新版本)。旧版 IE 不支持 CSS 动画相关特性。
怎么修改动画参数?
直接调整 CSS 中的 animation-duration(时长)、animation-timing-function(缓动曲线)等属性即可,代码中有注释说明。