当前位置: 首页 > news >正文

不定高元素动画实现方案(下)

前情

最近小程序接了一个需求,需要实现一个列表,列表可展开收起,展开收起需要有一个动画效果,而列表个数不定且每项内容高度也不固定,所以是一个不定高的收起展开效果,于是特意抽时间尝试了一些动画实现方案,特此记录

通过js+css变量来实现

实现思路是js获取要实现动画元素的高度,再通过css变量把高度设置在元素上,当hover的时候,把元素的高度设为css变量

关键代码如下:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>JS Bin</title>
</head>
<body><div class="container"><div class="inner"><div class="header">header</div><ul class="list" id="list" style="--height:0px"><li>scale111111111</li><li>scale2222222222</li><li>scale333333333</li><li>scale444444444</li></ul></div></div>
</body>
</html>
*{margin: 0;padding: 0;
}
.container{width: 100%;overflow: hidden;
}
.header{width: 100%;height: 48px;background-color: #ccc;display: flex;align-items: center;justify-content: center;
}
.list{background-color: green;height: var(--height)
}
document.addEventListener('DOMContentLoaded', () => {var list = document.querySelector('#list')list.style.setProperty('--height', list.scrollHeight + 'px')
})

演示地址:https://jsbin.com/besufuyihe/edit?html,css,js,output

20250920_200532

注意:

css动画使用比js要简单,同时性能上也会有优势,所以能用css实现的就尽量用css实现,此方式兼容性棒,JS干预也不是特别多

通过js+Flip动画来实现

FLIP是一种高性能动画技术,常用于实现复杂动画,代表四个步骤:

  1. First(初始):记录元素的初始状态
  2. Last(最终):设置元素到最终状态并记录
  3. Invert(反转):计算差异并将元素恢复到初始状态
  4. Play(播放):应用过渡效果并播放动画

代码如下:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>JS Bin</title>
</head>
<body><div class="container" id="container"><div class="inner"><div class="header">header</div><ul class="list"><li>Flip111111111</li><li>Flip2222222222</li><li>Flip333333333</li><li>Flip444444444</li></ul></div></div>
</body>
</html>
*{margin: 0;padding: 0;
}
.container{width: 100%;height: 48px;overflow: hidden;
}
.header{width: 100%;height: 48px;background-color: #ccc;display: flex;align-items: center;justify-content: center;
}
.list{background-color: green;
}
document.addEventListener('DOMContentLoaded', () => {// 获取容器元素const containero = document.querySelector('#container')// 鼠标进入容器时的FLIP动画实现containero.addEventListener('mouseenter', () => {// 第一步(F-First):记录初始状态(隐式的,初始高度为48px)// 第二步(L-Last):设置元素为最终状态,获取自然高度containero.style.height = 'auto';const conh = containero.scrollHeight; // 获取内容实际高度// 第三步(I-Invert):将元素恢复到初始状态containero.style.height = '48px';containero.offsetHeight; // 触发重绘,确保样式应用// 第四步(P-Play):添加过渡效果并播放动画到最终状态containero.style.transition = 'height .4s';containero.style.height = conh + 'px';})// 鼠标离开时恢复初始高度containero.addEventListener('mouseleave', () => {containero.style.height = '48px';})
})

演示地址:https://jsbin.com/barihazasi/edit?html,css,js,output

20250920_203017

注意:

Flip用于实现一些复杂动画是非常常见的方式,动画还是使用css3的transition,还是遵守一个原则,能用css实现动画的还是用css来做

纯js来实现

早期js实现动画都是使用setInterval/setTimeout来实现动画效果的,现在如果要用js来实现动画可以使用requestAnimationFrame来代替,来看一下他们的对比

1. 触发时机与刷新频率

  • setTimeout/setInterval
    • 按照指定的时间间隔(毫秒)触发回调函数
    • 时间间隔是近似值,实际执行会受 JavaScript 线程繁忙程度影响
    • 即使页面处于后台或隐藏状态,仍可能继续执行
    • 刷新频率固定,无法与显示器刷新率同步
  • requestAnimationFrame
    • 由浏览器决定执行时机,通常与显示器刷新率同步(60Hz 屏幕约每 16ms 执行一次)
    • 自动调整执行频率以匹配设备性能
    • 当页面处于后台或标签页隐藏时,会暂停执行以节省资源
    • 执行时机在浏览器重绘之前,确保动画平滑

2. 性能表现

  • setTimeout/setInterval
    • 可能导致动画卡顿或跳帧,因为无法与浏览器渲染周期对齐
    • 多个定时器同时运行时可能导致性能问题
    • 高频率定时器 (如 10ms) 可能导致浏览器过度渲染,消耗不必要的资源
  • requestAnimationFrame
    • 浏览器会优化动画执行,确保流畅性
    • 自动调整帧率,在性能不足时降低频率
    • 不会在页面不可见时执行,节省 CPU/GPU 资源

关键代码如下:

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width"><title>JS Bin</title>
</head>
<body><div class="container" id="container"><div class="inner"><div class="header">header</div><ul class="list"><li>js111111111</li><li>js2222222222</li><li>js333333333</li><li>js444444444</li></ul></div></div>
</body>
</html>
*{margin: 0;padding: 0;
}
.container{width: 100%;height: 48px;overflow: hidden;
}
.header{width: 100%;height: 48px;background-color: #ccc;display: flex;align-items: center;justify-content: center;
}
.list{background-color: green;
}
document.addEventListener('DOMContentLoaded', () => {const containero = document.querySelector('#container')// 初始高度为48px(与CSS中定义的一致)const initH = 48;// 获取容器完全展开时的高度const conh = containero.scrollHeight;// 每一帧增加/减少的高度像素const step = 5;// 标记动画是否正在运行,防止多次触发let isAnimating = false;containero.addEventListener('mouseenter', () => {// 如果已经在动画中,则不重新开始if (isAnimating) return;// 获取当前高度作为起点let currentHeight = parseInt(containero.offsetHeight);isAnimating = true;const expand = function () {// 增加高度currentHeight += step;// 检查是否达到目标高度if (currentHeight >= conh) {containero.style.height = conh + 'px';isAnimating = false;return;}// 设置新高度并继续动画containero.style.height = currentHeight + 'px';requestAnimationFrame(expand);}// 开始展开动画requestAnimationFrame(expand);})// 鼠标离开时恢复初始高度containero.addEventListener('mouseleave', () => {// 如果已经在动画中,则不重新开始if (isAnimating) return;// 获取当前高度作为起点let currentHeight = parseInt(containero.offsetHeight);isAnimating = true;const collapse = function () {// 减少高度currentHeight -= step;// 检查是否达到初始高度if (currentHeight <= initH) {containero.style.height = initH + 'px';isAnimating = false;return;}// 设置新高度并继续动画containero.style.height = currentHeight + 'px';requestAnimationFrame(collapse);}// 开始收起动画requestAnimationFrame(collapse);})
})

演示地址:https://jsbin.com/kunuqomale/edit?html,css,js,output

20250920_210140

注意:

早期想实现动画都是依赖js来做的,看这代码量就知道用JS实现既复杂又性能一般,现在css3动画已经兼容性非常好了,推荐用css3动画来实现,使用js来实现动画是最终选择了,当然真正项目开发也不会自己去写js动画函数了,市面上有很多强大易用的动画库,早期jQuery就自带动画函数,还有我觉得市面上最强大的动画库GSAP是目前js实现动画的首选了

总结

对于做技术的我们,每天都是提出问题解决问题的一个过程,过程中会尝试各种方案,因为解决问题的方案千千万,每种方案都有自己的适合场景,此篇文章记录了实现不定高内容过渡效果的另外三种实现方式,上中下篇文章一共介绍了9种实现动画的方式

个人知识有限,如果你有更好的实现方案,希望不吝分享,一起学习一起进步

http://www.hskmm.com/?act=detail&tid=14588

相关文章:

  • 详细介绍:C 语言:第 20 天笔记:typedef(类型重命名规则、应用场景与实战案例)
  • Screaming Architecture:让架构自己说话
  • BOE(京东方)携手UNESCO联合主办WCBR“科学十年”分会 彰显中国科技企业可持续发展实力
  • 使用Cyclops.PdfKit根据pdf模板生成pdf文件
  • 一款文本编辑器的介绍
  • 随笔-决战保研篇
  • 科研人必知:293F与HEK293细胞在蛋白表达中的不同“超能力”
  • Redis Cluster
  • 如何使用C语言实现Vigenre密码加解密
  • 【F#学习】列表 List
  • Trae与Gitee MCP深度集成:AI编程工具链迎来重大升级
  • 【2025-09-22】加班感悟
  • OpenAI Codex 使用 智谱 API
  • 嵌入式ARM架构学习9——IIC - 教程
  • Day04---数据类型及面试题详解
  • 记-一次H3C交换机版本升级
  • 客服系统中的定时任务设计与实现
  • 使用Go语言实现高效定时任务功能
  • JavaScript获取NHK的附件文件
  • 承兑 背书 贴现区别
  • 洛谷题单指南-进阶数论-P3811 【模板】模意义下的乘法逆元
  • Interlocked.Increment学习
  • 基于解析法的四轴SCARA机器人正逆运动学代码
  • .Net-IIS 文件上传安全漏洞问题
  • 【F#学习】记录 Record
  • 【光照】[高光反射specular]以UnityURP为例
  • 游戏性能优化与逆向分析技术
  • 使用 feign 调用时对微服务实例进行选择
  • EI目录今年第3次更新!55本中国期刊被收录,附完整版下载
  • 程序员的未来:从技术岗位到全栈思维的进化之路 - 实践