寬高爲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開發的話,需要寫摺疊面板組件,可以參考我的這篇文章:添加鏈接描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章