如何实现点击按钮带高度动画展开段落文本且适配响应式
实现原理说明
height: auto无法触发CSS过渡的核心原因是浏览器计算过渡差值时,无法解析auto对应的明确像素值,会直接跳过动画流程;写死固定展开高度的方案天然不支持响应式——不同视口宽度下文本换行逻辑不同,内容总高度是动态变化的,固定值必然会出现内容截断或者留白问题。
以下提供两种可直接落地的实现方案:
方案1:JS动态获取内容真实高度(最优解,适配全场景)
核心逻辑是不直接用height: auto触发展开,而是通过DOM原生属性scrollHeight拿到内容的实际渲染高度,把明确像素值设为容器高度触发过渡,动画结束后再把高度切回auto避免窗口缩放时内容被截断;折叠时先把当前auto高度转成明确像素值,下一帧再切回折叠高度,保证反向动画也能正常触发。
- 优点:动画时长完全精准,和设置的过渡参数100%匹配;全响应式适配,窗口缩放时自动调整高度;不需要提前预估内容最大高度,适配任意长度的文本
调整后的SCSS代码
.about__body{ @include displayFlex($direction: column, $align-item: center); gap: 20px; &-content { height: 60px; overflow: hidden; transition: height 400ms ease-in-out; // 只过渡height属性,性能更好 } } /* 展开类不需要写height值,所有高度逻辑交给JS控制 */
调整后的JS代码
const btnAbout = document.getElementById("btn--about") const contentAbout = document.querySelector(".about__body-content") const COLLAPSE_HEIGHT = 60 // 和CSS里配置的折叠高度保持一致 const toggleContent = () => { const isExpanded = contentAbout.classList.contains('about__body-content-show') if (isExpanded) { // 折叠逻辑:先把当前auto高度转成明确像素值,否则过渡无法触发 contentAbout.style.height = `${contentAbout.scrollHeight}px` // 触发重绘后下一帧设置折叠高度 requestAnimationFrame(() => { contentAbout.style.height = `${COLLAPSE_HEIGHT}px` contentAbout.classList.remove('about__body-content-show') }) } else { // 展开逻辑:先加展开类标记状态,再设置真实内容高度触发过渡 contentAbout.classList.add('about__body-content-show') const contentRealHeight = contentAbout.scrollHeight contentAbout.style.height = `${contentRealHeight}px` // 过渡结束后把高度设为auto,适配窗口大小变化 const onTransitionEnd = () => { contentAbout.style.height = 'auto' contentAbout.removeEventListener('transitionend', onTransitionEnd) } contentAbout.addEventListener('transitionend', onTransitionEnd) } } // 监听窗口尺寸变化,展开状态下同步更新容器高度避免截断 window.addEventListener('resize', () => { const isExpanded = contentAbout.classList.contains('about__body-content-show') if (!isExpanded) return // 临时取消过渡,直接更新为新的内容高度 contentAbout.style.transition = 'none' contentAbout.style.height = 'auto' const newHeight = contentAbout.scrollHeight contentAbout.style.height = `${newHeight}px` // 恢复过渡配置 requestAnimationFrame(() => { contentAbout.style.transition = 'height 400ms ease-in-out' }) }) btnAbout.addEventListener('click', (e) => { e.preventDefault() toggleContent() })
方案2:纯CSS实现(无需修改JS,适合简单场景)
把过渡属性从height换成max-height,折叠状态设置max-height: 60px,展开状态设置一个一定大于内容最大可能高度的max-height值即可触发过渡,原有JS切换类的逻辑完全不需要改动。
- 缺点:需要提前预估内容的最大高度,值设小了会截断内容,设大了动画速率会失真(浏览器按设置的max-height差值计算动画进度,不是真实内容高度);反向折叠动画速度和展开时会有细微差异
SCSS代码
.about__body{ @include displayFlex($direction: column, $align-item: center); gap: 20px; &-content { max-height: 60px; overflow: hidden; transition: max-height 400ms ease-in-out; } } .about__body-content-show { // 根据自身内容的最大可能高度设置,比如内容最多不超过500px就填500px max-height: 500px; }
可选体验优化
折叠状态可以给容器底部加半透明渐变遮罩,更明确提示用户下方还有未展示内容:
.about__body-content:not(.about__body-content-show) { -webkit-mask-image: linear-gradient(to bottom, #000 70%, transparent 100%); mask-image: linear-gradient(to bottom, #000 70%, transparent 100%); }
内容的提问来源于stack exchange,提问作者Booskap13
相关产品推荐
相关产品推荐

