CSS扩展/收缩动画以显示/隐藏内容

11

我正在尝试创建一个可以通过简单的滑动动画展开和折叠的框。如果您运行下面的示例,其想法是它从一个红线开始,当您单击按钮时,它分成两条红线并缓慢扩展以显示内容,就像从桌子上拉出抽屉一样。

我已经尝试了transform、animation、relative: positioning with top等方法,但无法获得所需的效果。

包含框应该扩大大小。

function expandContract() {
   const el = document.getElementById("expand-contract")
   el.classList.toggle('expanded')
   el.classList.toggle('collapsed')
}
#container {
   border: 1px solid black;
   padding: 15px;
}

#top-section {
  border-bottom: 1px solid red;
}

#expand-contract {
  border-bottom: 1px solid red;
}

.expand-contract {
   transform: translateY(-100%)
   overflow: hidden;
}

@keyframes slide-in {
    100% {
        transform: translateY(0%)
    }
}

.expanded {
   background-color: green;
   animation-name: slide-in;
   animation-duration: 1s;
}

.collapsed {
   background-color: red;
   transform: translateY(-100%)
}
<div id="container">
  <div id="top-section">
    This is always displayed
  </div>
  
  <div id="expand-contract" class="expanded">
    This section expands and contracts
  
    <table>
      <tr><td>test1</td></tr>
      <tr><td>test2</td></tr>
      <tr><td>test3</td></tr>
      <tr><td>test4</td></tr>
    </table>
  </div>
  
  <div id="bottom-section">
    This section is always displayed
  </div>
</div>

<button onclick="expandContract()">Expand/Contract</button>

2个回答

24
使用 CSS transition 和切换的样式,可以实现此功能。最初您可能会认为需要过渡高度(从0initial,以便根据高度动态扩展),但不幸的是,CSS transition 无法正确处理此操作。
相反,您可以将其包装在具有 overflow: hidden 的自己的容器中,然后使用 margin-top: -100% 隐藏它,并使用 0 显示它。
这是带有此修改的代码:

function expandContract() {
   const el = document.getElementById("expand-contract")
   el.classList.toggle('expanded')
   el.classList.toggle('collapsed')
}
#container {
   border: 1px solid black;
   padding: 15px;
}

#top-section {
  border-bottom: 1px solid red;
}

#expand-container {
  overflow: hidden;
}

#expand-contract {
  border-bottom: 1px solid red;
  margin-top: -100%;
  transition: all 1s;
}

#expand-contract.expanded {
   background-color: green;
   margin-top: 0;
}
<div id="container">
  <div id="top-section">
    This is always displayed
  </div>
  
  <div id="expand-container">
    <div id="expand-contract" class="expanded">
      This section expands and contracts
  
      <table>
        <tr><td>test1</td></tr>
        <tr><td>test2</td></tr>
        <tr><td>test3</td></tr>
        <tr><td>test4</td></tr>
      </table>
    </div>
  </div>
  
  <div id="bottom-section">
    This section is always displayed
  </div>
</div>

<button onclick="expandContract()">Expand/Contract</button>


4
当我收缩盒子后再点击展开时,它需要1000毫秒的延迟才会展开。你知道是什么原因导致的吗? - Tony J Watson
过渡效果:所有1秒钟>>增加1秒的延迟。将其更改为过渡效果:1秒钟全部0秒;以修复。 - Tony J Watson
6
使用 margin-top: -100%; 表示 -100% 将相对于包含盒子的 宽度 进行计算(在这种情况下,是 #expand-container),因此该技术仅在要显示或隐藏的元素比父元素窄时才能正常工作。请参见:https://dev59.com/fVsW5IYBdhLWcg3wqo0x - Dai
@TonyJWatson,你的解决方案导致转换根本无法工作。 - allenlin1992
@TonyJWatson 延迟是由于初始的 margin-top 是容器宽度的 -100%,因此动画需要时间才能变得可见。你的修复将动画持续时间设置为 0,从而有效地禁用了它。 - Herman Kan
正如@Dai所建议的那样,这个答案存在一些问题,动画有点古怪,并且只适用于正方形div(实际动画时间也不会是1秒,具体取决于内容高度)。我建议查看这篇博客以获取一些选项(个人偏爱第三个选项,不幸的是它涉及JavaScript和布局抖动)https://carlanderson.xyz/how-to-animate-on-height-auto/ - Noel De Martin

4

hope to help you

HTML

<div class="container">
    <div id="top-section">
      This is always displayed
    </div>
   <div id="expand-container">
    <div class="expanded" id="expand-contract">
         <table>
            <tr><td>test1</td></tr>
            <tr><td>test2</td></tr>
            <tr><td>test3</td></tr>
            <tr><td>test4</td></tr>
        </table>
    </div>
  </div>
</div>
<button class="header" onclick="expandContract()">Expand/Contract</button>

css

.container {
    width:100%;
    border:1px solid #d3d3d3;
}
.container div {
    width:100%;
}
.header {
    background-color:#d3d3d3;
    padding: 2px;
    cursor: pointer;
    font-weight: bold;
}
.container .expanded {
    display: none;
    padding : 5px;
}

js

function expandContract() {
    $header = $(".header");
    $content = $("#expand-contract")
    $content.slideToggle(500, function () {
        $header.text(function () {
            return $content.is(":visible") ? "Collapse" : "Expand";
        });
    });
};

点击这里查看代码。


7
“希望能够帮到你”这样的话很好,但是如果你真的想帮助别人,在每个回答中都包含一个教育性的解释会更有助于理解。 - mickmackusa

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接