宽高为auto的元素如何设置动画(折叠面板)

在 css 属性为 auto 的元素不能设置动画,设置了动画也不会有效果,比如下面代码:

.dropdown {
    
    
  transition: 0.2s;
  height: 0;
}
.dropdown.open {
    
    
  /* 高度发生了改变,但是没有动画. */
  height: auto;
}

折叠面板大家都知道,就是希望有一个元素能够使用CSS transition平滑地折叠和展开,但是它的展开大小需要依赖于里面的内容。

已经设置了过渡:transition: 0.2s ,但是展开动画并没有生效。发现只有当高度为auto自动计算时,才会出现此问题。百分比,像素值,任何绝对单位都能正常工作。但是所有这些都需要预先设置一个特定的高度,而不是让它自然地由元素内容的大小决定。

为什么浏览器不去修复这个问题?

根据Mozilla开发者文档,auto值被有意地排除在 CSS transition 动画属性规范之外。参考这个文档可以查询到支持的属性有哪些:传送门。完整内容查看这里MDN:链接

我们都知道“回流”的概念,重新计算所有元素的大小和位置在浏览器中非常消耗性能。如果要将一个元素transition为auto的高度,浏览器必须对该动画的每个阶段执行回流,以确定所有其他元素应该如何移动。它不能以简单的方式缓存或计算,因为展开的时候不知道具体可以展开到多大的高度。这将使浏览器必须在幕后进行的数学计算变得复杂,并且可能以不明显的方式降低性能。

如果解决这个问题呢?下面有几种实现的思路给大家参考。

max-height

前文已经提到了,CSS值只能在固定单位值之间转换。但是假设我们有一个元素,它的height被设置为auto,但是它的max height被设置为一个固定值,比如1000px。我们不能转换高度,但我们可以转换最大高度,因为它有一个显式的值。在任何时候,元素的实际高度将是高度和最大高度的最大值决定的。所以只要max height的值大于auto的值,我们就可以转换max height并获得所需的效果。

这有两个关键的缺点:1、必须提前预测出高度。2、建议使用匀速动画。

transform: scaleY()

实现是这样的:我们为元素的transform属性设置一个转换,然后在 transform:scaleY(1)和transform:scaleY(0)之间切换。这分别意味着,“与开始时相同的比例(在y轴上)呈现该元素”和“以0的比例(在y轴上)呈现该元素”。在这两种状态之间的转换将巧妙地“挤压”元素的自然大小和基于内容的大小。缺点呢?由于不会触发回流,因此此元素周围的元素将完全不受影响。也就是说当元素展开的时候,后面的元素不会往下移动。

这种方案适合在定位的元素中使用,比如element-ui中的select组件就是基于这种方案实现的下拉展开。

JavaScript

最后就是JS的实现方案了,效果也是最佳的,但是实现代码有一定的难度。

如果您绝对需要平滑地过渡折叠部分,其扩展大小完全由内容决定,并且页面上的其他元素在转换时会自动伸缩,那么您可以使用一些JavaScript来实现这一点。

<div class="container">
  <div class="section">
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
  </div>
  <div class="section collapsible">
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p>
  </div>
  <div class="section">
   <p>Ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p>
  </div>
</div>

<button id="toggle-button">Toggle collapse</button>

JS

function collapseSection(element) {
  var sectionHeight = element.scrollHeight;
  
  var elementTransition = element.style.transition;
  element.style.transition = '';
  
  requestAnimationFrame(function() {
    element.style.height = sectionHeight + 'px';
    element.style.transition = elementTransition;
    
    requestAnimationFrame(function() {
      element.style.height = 0 + 'px';
    });
  });
  
  element.setAttribute('data-collapsed', 'true');
}

function expandSection(element) {
  var sectionHeight = element.scrollHeight;
  element.style.height = sectionHeight + 'px';

  element.addEventListener('transitionend', function(e) {
    element.removeEventListener('transitionend', arguments.callee);
    element.style.height = null;
  });
  
  element.setAttribute('data-collapsed', 'false');
}

document.querySelector('#toggle-button').addEventListener('click', function(e) {
  var section = document.querySelector('.section.collapsible');
  var isCollapsed = section.getAttribute('data-collapsed') === 'true';
    
  if(isCollapsed) {
    expandSection(section)
    section.setAttribute('data-collapsed', 'false')
  } else {
    collapseSection(section)
  }
});

最后如果你是使用Vue开发的话,需要写折叠面板组件,可以参考我的这篇文章:添加链接描述

猜你喜欢

转载自blog.csdn.net/wu_xianqiang/article/details/107055672