展開收起效果是比較常見的一種交互方式,一般的作法是控制display
屬性值在none
和其它值之間切換,雖然說功能能夠實現,可是效果略顯生硬,因此會有這樣的需求——但願元素展開收起能具備平滑的效果。javascript
首先想到的是經過height
在0
與auto
之間切換,可是結果可能並不會是咱們所預期的那樣,緣由是咱們將要展開的元素內容是動態的,即高度值不肯定,所以height
使用的值是默認的auto
,從0px
到auto
是沒法計算的,所以沒法造成過渡或動畫效果。
據此咱們可使用max-height
,將max-height
從0
過渡到一個可以大於徹底顯示內部元素的值,展開後的max-height值,只須要設定爲保證比展開內容高度大的值便可,在max-height
值比height
值大的狀況下,元素仍會默認採用自身的高度值即auto
,如此一來一個高度不定的元素展開收起動畫效果就實現了。
請注意這種方式實現仍是有限制的,使用CSS
進行過渡動畫的時候依舊是經過計算0
到設定的max-height
高度值進行計算的,在實際應用中若是max-height
值太大,在元素收起的時候將會產生延遲的效果,這是由於在收起時,max-height
從設定的特別大的值,到元素自身高度值的變化過程將佔用較多時間,此時畫面表現則會產生延遲的效果。所以建議將max-height
值設置爲足夠安全的最小值,這樣在收起時即便有略微延遲,也會由於時間很短,難以被用戶感知,將不會影響體驗。css
<!DOCTYPE html> <html> <head> <title>展開動畫</title> <style type="text/css"> .page{ width: 200px; padding: 10px 20px; border: 1px solid #eee; } .container { overflow: hidden; } .container > .options{ transition: all .5s; max-height: 0; } .container > .unfold{ max-height: 150px; } .container > .btn{ color: #4C98F7; cursor: pointer; text-decoration: underline; } </style> </head> <body> <div class="page"> <div class="container"> <div class="btn" onclick="operate(this)" unfold="1">展開</div> <div class="options"> <div class="option">選項1</div> <div class="option">選項2</div> <div class="option">選項3</div> <div class="option">選項4</div> <div class="option">選項5</div> <div class="option">選項6</div> <div class="option">選項7</div> </div> </div> </div> </body> <script type="text/javascript"> function operate(btn){ const optionsNode = document.querySelector(".container > .options"); const unfold = btn.getAttribute("unfold"); if(unfold && unfold==="1"){ btn.innerText = "收縮"; optionsNode.classList.add("unfold"); }else{ btn.innerText = "展開"; optionsNode.classList.remove("unfold"); } btn.setAttribute("unfold", unfold === "0" ? "1" : "0"); } </script> </html>
使用max-height
一定有必定的侷限性,那麼不如咱們在DOM
加載完成後就取得元素的實際高度並保存,以後直接利用這個真實高度與0
進行動畫過渡便可,由於瀏覽器的渲染順序,在解析JavaScript
時會阻塞DOM
的渲染,因此在獲取元素實際高度再設置高度爲0
的過程當中通常不會出現閃爍的狀況,若是實在擔憂由於獲取高度以後再將高度設置爲0
可能會有一個閃爍的過程,那麼咱們能夠取得元素父節點後調用cloneNode(true)
方法或者innerHTML
方法取得字符串再innerHTML
到一個新建立的節點,目的就是將其拷貝,以後將其使用絕對定位等放置到屏幕外即將其設置到屏幕可以顯示的外部區域,注意此時要設置body
的overflow: hidden;
,以後利用getComputedStyle
取得實際高度,而後再將其移出DOM
結構,此時有了實際高度就能夠進行動畫過渡了,下面簡單的實現一下在DOM
加載時便取得實際高度進行動畫實現。html
<!DOCTYPE html> <html> <head> <title>展開動畫</title> <style type="text/css"> .page{ width: 200px; padding: 10px 20px; border: 1px solid #eee; } .container { overflow: hidden; } .container > .options{ transition: all .5s; } .container > .btn{ color: #4C98F7; cursor: pointer; text-decoration: underline; } </style> </head> <body> <div class="page"> <div class="container"> <div class="btn" onclick="operate(this)" unfold="1">展開</div> <div class="options"> <div class="option">選項1</div> <div class="option">選項2</div> <div class="option">選項3</div> <div class="option">選項4</div> <div class="option">選項5</div> <div class="option">選項6</div> <div class="option">選項7</div> </div> </div> </div> </body> <script type="text/javascript"> (function init(win, doc){ const optionsNode = document.querySelector(".container > .options"); optionsNode.setAttribute("real-height", win.getComputedStyle(optionsNode).height); optionsNode.style.height = "0px"; })(window, document); function operate(btn){ const optionsNode = document.querySelector(".container > .options"); const unfold = btn.getAttribute("unfold"); const realHeight = optionsNode.getAttribute("real-height"); if(unfold && unfold==="1"){ btn.innerText = "收縮"; optionsNode.style.height = realHeight; }else{ btn.innerText = "展開"; optionsNode.style.height = "0px"; } btn.setAttribute("unfold", unfold === "0" ? "1" : "0"); } </script> </html>
還有一種經常使用實現動畫的方式,即首先將外層元素沒有動畫過渡的形式直接展開,再將選項加入動畫緩慢下落,一般利用transform: translateY();
去實現這個緩慢降低的動畫,在微信的WEUI
小程序組件庫的首頁就是採用這種實現方式。java
<!DOCTYPE html> <html> <head> <title>展開動畫</title> <style type="text/css"> .page{ width: 200px; padding: 10px 20px; border: 1px solid #eee; } .container, .options-container { overflow: hidden; } .options-container{ height: 0; } .container .options{ transition: all .5s; transform: translateY(-100%); } .container .unfold{ transform: translateY(0); } .container > .btn{ color: #4C98F7; cursor: pointer; text-decoration: underline; } </style> </head> <body> <div class="page"> <div class="container"> <div class="btn" onclick="operate(this)" unfold="1">展開</div> <div class="options-container"> <div class="options"> <div class="option">選項1</div> <div class="option">選項2</div> <div class="option">選項3</div> <div class="option">選項4</div> <div class="option">選項5</div> <div class="option">選項6</div> <div class="option">選項7</div> </div> </div> </div> </div> </body> <script type="text/javascript"> function operate(btn){ const optionsNode = document.querySelector(".container .options"); const optionsContainer = document.querySelector(".options-container"); const unfold = btn.getAttribute("unfold"); if(unfold && unfold==="1"){ btn.innerText = "收縮"; optionsNode.classList.add("unfold"); optionsContainer.style.height = "auto"; }else{ btn.innerText = "展開"; optionsNode.classList.remove("unfold"); optionsContainer.style.height = "0px"; } btn.setAttribute("unfold", unfold === "0" ? "1" : "0"); } </script> </html>
https://github.com/WindrunnerMax/EveryDay
http://www.111com.net/wy/192615.htm https://zhuanlan.zhihu.com/p/52582555 https://cloud.tencent.com/developer/article/1499033