找回密码
 立即注册
首页 业界区 业界 不定高元素动画实现方案(下)

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

咸和璧 2025-9-23 11:49:02
前情

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

实现思路是js获取要实现动画元素的高度,再通过css变量把高度设置在元素上,当hover的时候,把元素的高度设为css变量
关键代码如下:
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.   <meta charset="utf-8">
  5.   <meta name="viewport" content="width=device-width">
  6.   <title>JS Bin</title>
  7. </head>
  8. <body>
  9.   
  10.    
  11.       header
  12.       <ul  id="list" >
  13.         <li>scale111111111</li>
  14.         <li>scale2222222222</li>
  15.         <li>scale333333333</li>
  16.         <li>scale444444444</li>
  17.       </ul>
  18.    
  19.   
  20. </body>
  21. </html>
复制代码
  1. *{
  2.   margin: 0;
  3.   padding: 0;
  4. }
  5. .container{
  6.   width: 100%;
  7.   overflow: hidden;
  8. }
  9. .header{
  10.   width: 100%;
  11.   height: 48px;
  12.   background-color: #ccc;
  13.   display: flex;
  14.   align-items: center;
  15.   justify-content: center;
  16. }
  17. .list{
  18.   background-color: green;
  19.   height: var(--height)
  20. }
复制代码
  1. document.addEventListener('DOMContentLoaded', () => {
  2.   var list = document.querySelector('#list')
  3.   list.style.setProperty('--height', list.scrollHeight + 'px')
  4. })
复制代码
演示地址:https://jsbin.com/besufuyihe/edit?html,css,js,output
1.gif

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

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

  • First(初始):记录元素的初始状态
  • Last(最终):设置元素到最终状态并记录
  • Invert(反转):计算差异并将元素恢复到初始状态
  • Play(播放):应用过渡效果并播放动画
代码如下:
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.   <meta charset="utf-8">
  5.   <meta name="viewport" content="width=device-width">
  6.   <title>JS Bin</title>
  7. </head>
  8. <body>
  9.   
  10.    
  11.       header
  12.       <ul >
  13.         <li>Flip111111111</li>
  14.         <li>Flip2222222222</li>
  15.         <li>Flip333333333</li>
  16.         <li>Flip444444444</li>
  17.       </ul>
  18.    
  19.   
  20. </body>
  21. </html>
复制代码
  1. *{
  2.    margin: 0;
  3.    padding: 0;
  4. }
  5. .container{
  6.   width: 100%;
  7.   height: 48px;
  8.   overflow: hidden;
  9. }
  10. .header{
  11.   width: 100%;
  12.   height: 48px;
  13.   background-color: #ccc;
  14.   display: flex;
  15.   align-items: center;
  16.   justify-content: center;
  17. }
  18. .list{
  19.   background-color: green;
  20. }
复制代码
  1. document.addEventListener('DOMContentLoaded', () => {
  2.   // 获取容器元素
  3.   const containero = document.querySelector('#container')
  4.   
  5.   // 鼠标进入容器时的FLIP动画实现
  6.   containero.addEventListener('mouseenter', () => {
  7.     // 第一步(F-First):记录初始状态(隐式的,初始高度为48px)
  8.    
  9.     // 第二步(L-Last):设置元素为最终状态,获取自然高度
  10.     containero.style.height = 'auto';
  11.     const conh = containero.scrollHeight; // 获取内容实际高度
  12.    
  13.     // 第三步(I-Invert):将元素恢复到初始状态
  14.     containero.style.height = '48px';
  15.     containero.offsetHeight; // 触发重绘,确保样式应用
  16.    
  17.     // 第四步(P-Play):添加过渡效果并播放动画到最终状态
  18.     containero.style.transition = 'height .4s';
  19.     containero.style.height = conh + 'px';
  20.   })
  21.   // 鼠标离开时恢复初始高度
  22.   containero.addEventListener('mouseleave', () => {
  23.     containero.style.height = '48px';
  24.   })
  25. })
复制代码
演示地址:https://jsbin.com/barihazasi/edit?html,css,js,output
2.gif

注意:
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 资源

关键代码如下:
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4.   <meta charset="utf-8">
  5.   <meta name="viewport" content="width=device-width">
  6.   <title>JS Bin</title>
  7. </head>
  8. <body>
  9.   
  10.    
  11.       header
  12.       <ul >
  13.         <li>js111111111</li>
  14.         <li>js2222222222</li>
  15.         <li>js333333333</li>
  16.         <li>js444444444</li>
  17.       </ul>
  18.    
  19.   
  20. </body>
  21. </html>
复制代码
  1. *{
  2.   margin: 0;
  3.   padding: 0;
  4. }
  5. .container{
  6.   width: 100%;
  7.   height: 48px;
  8.   overflow: hidden;
  9. }
  10. .header{
  11.   width: 100%;
  12.   height: 48px;
  13.   background-color: #ccc;
  14.   display: flex;
  15.   align-items: center;
  16.   justify-content: center;
  17. }
  18. .list{
  19.   background-color: green;
  20. }
复制代码
[code]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

相关推荐

您需要登录后才可以回帖 登录 | 立即注册