Skip to content

CSS高度自适应过渡效果实现指南 🎨 ​

在网页开发中,实现元素高度从0到auto的平滑过渡是一个常见需求。本文将介绍几种实现这种效果的方案,并分析各自的优缺点。

1. 使用max-height实现 📏 ​

这是最传统和兼容性最好的方案。

实现原理 ​

  • 通过设置一个足够大的max-height值
  • 利用max-height的过渡效果
  • 初始状态max-height为0

优缺点 ​

✅ 优点:

  • 兼容性极好
  • 实现简单

❌ 缺点:

  • 过渡时间可能不够精确
  • max-height值需要预估

演示示例 ​

利用max-height

代码实现 ​

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .container {
      width: 100%;
      height: 500px;
      display: flex;
      flex-direction: column;
      align-items: center;
      position: relative;
    }

    button {
      padding: 10px 20px;
      border-radius: 5px;
      color: #fff;
      background-color: #000;
    }

    button:hover~.content {
      /* height: 300px; */
      max-height: 300px;
    }

    .content {
      transition: all .3s;
      width: 300px;
      background: #000;
      color: #fff;
      overflow: hidden;
      /* height: 0; */
      max-height: 0;
    }

    p {
      padding: 10px 20px;
    }
  </style>
</head>

<body>
  <div class="container">
    <button>高度自适应的Hover</button>
    <div class="content">
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.</p>
    </div>
  </div>
</body>

</html>

2. 使用calc-size方案 🔢 ​

这是一个较新的CSS特性。

注意事项 ​

⚠️ 需要注意:

  • 浏览器兼容性有限
  • 需要两个参数才能工作
  • 不兼容时内容可能无法显示

代码实现 ​

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .container {
      width: 100%;
      height: 500px;
      display: flex;
      flex-direction: column;
      align-items: center;
      position: relative;
    }

    button {
      padding: 10px 20px;
      border-radius: 5px;
      color: #fff;
      background-color: #000;
    }

    button:hover~.content {
      /* 一个参数已经不支持了,需要两个参数  如果不兼容则无法显示内容*/
      height: calc-size(auto, size);
    }

    .content {
      transition: all .3s;
      width: 300px;
      background: #000;
      color: #fff;
      overflow: hidden;
      height: 0;
    }

    p {
      padding: 10px 20px;
    }
  </style>
</head>

<body>
  <div class="container">
    <button>高度自适应的Hover</button>
    <div class="content">
      <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.</p>
    </div>
  </div>
</body>

</html>

3. interpolate-size方案 📐 ​

特点 ​

  • 允许使用auto等关键字
  • 如果不兼容是可以显示内容的,但是无法过渡
css
button:hover~.content {
  height: auto;
}

.content {
  transition: all .3s;
  width: 300px;
  background: #000;
  color: #fff;
  overflow: hidden;
  height: 0;
  /* 允许使用关键词  */
  interpolate-size: allow-keywords;
}

4. Grid布局方案 ⭐️ ​

这是目前最推荐的现代解决方案!

实现原理 ​

  • 利用grid-template-rows的fr单位
  • 通过0fr到1fr的过渡实现平滑效果

优点 ​

✅ 优势:

  • 完美的过渡效果
  • 无需预设高度
  • 代码简洁优雅
  • 性能好

演示示例 ​

利用grid布局

代码实现 ​

html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .container {
      width: 100%;
      height: 500px;
      display: flex;
      flex-direction: column;
      align-items: center;
      position: relative;
    }

    button {
      padding: 10px 20px;
      border-radius: 5px;
      color: #fff;
      background-color: #000;
    }

    button:hover~.content {
      grid-template-rows: 1fr;
    }

    .content {
      box-sizing: border-box;
      display: grid;
      /* 对剩余可用空间进行分配 */
      grid-template-rows: 0fr;
      transition: all .3s;
      width: 300px;
      background: #000;
      color: #fff;
      overflow: hidden;
    }

    /* 注意多加一层 */
    .content-item { 
      margin: 0;
      min-height: 0;
      padding: 0 20px;
    }
  </style>
</head>

<body>
  <div class="container">
    <button>高度自适应的Hover</button>
    <div class="content">
      <div class="content-item">
        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.</p>
      </div>
    </div>
  </div>
</body>

</html>

5. JavaScript方案 💻 ​

当需要更精确的控制时,可以使用JS实现。

实现要点 ​

  • 获取实际高度
  • 使用requestAnimationFrame优化动画
  • 注意强制重绘的时机

演示示例 ​

JS实现高度自适应过渡

代码实现 ​

html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>JS实现高度自适应过渡</title>
    <style>
        .container {
            width: 100%;
            display: flex;
            flex-direction: column;
            align-items: center;
            padding: 20px;
        }

        button {
            padding: 10px 20px;
            border-radius: 5px;
            color: #fff;
            background-color: #000;
            margin-bottom: 10px;
        }

        .content {
            width: 300px;
            background: #000;
            color: #fff;
            overflow: hidden;
            height: 0;
        }

        .content-inner {
            padding: 20px;
        }
    </style>
</head>
<body>
    <div class="container">
        <button onclick="toggleContent()">点击展开/收起</button>
        <div class="content" id="content">
            <div class="content-inner">
                <p>这是一段测试文本,用来演示高度自适应过渡效果。</p>
                <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.</p>
            </div>
        </div>
    </div>

    <script>
        let isOpen = false;
        const content = document.getElementById('content');

        function toggleContent() {
            if (!isOpen) {
                // 展开内容
                content.style.height = 'auto';  // 临时设置auto以获取实际高度
                const actualHeight = content.offsetHeight;  // 获取实际高度
                content.style.height = '0';  // 重置高度为0
                
                // 强制重绘
                content.offsetHeight;
                
                // 设置过渡效果并展开到实际高度
                content.style.transition = 'height 0.3s ease';
                content.style.height = actualHeight + 'px';
            } else {
                // 收起内容
                content.style.transition = 'height 0.3s ease';
                content.style.height = '0';
            }
            
            isOpen = !isOpen;
        }
    </script>
</body>
</html>

react 版本核心代码

js
      useEffect(() => {
        const dom = mainRef.current
        if (showExtra) {
          dom.style.height = 'auto'
          const { height } = dom.getBoundingClientRect()
          dom.style.height = 0
          dom.style.transition = '.3s'
          dom.offsetHeight   //强制重绘
          dom.style.height = Math.floor(height + 30) + 'px'
        } else {
          dom.style.height = 0
          dom.style.transition = '.3s'
        }
      }, [showExtra])

补充说明 📝 ​

选择建议 ​

  1. 现代项目优先选择Grid方案
  2. 需要广泛兼容时使用max-height方案
  3. 复杂交互场景考虑JS方案

性能优化提示 ​

  • 使用transform替代height过渡
  • 添加will-change提示
  • 避免频繁触发重排

常见陷阱 ​

⚠️ 注意事项:

  • height:auto不支持过渡
  • 内容动态变化时需要重新计算
  • 嵌套动画可能导致性能问题

浏览器兼容性 🌐 ​

方案ChromeFirefoxSafariEdge
max-height✅✅✅✅
grid✅✅✅✅
calc-size⚠️⚠️❌⚠️
JS方案✅✅✅✅

参考资源 📚 ​