天天看點

CSS實作展開動畫

CSS實作展開動畫

展開收起效果是比較常見的一種互動方式,通常的做法是控制

display

屬性值在

none

和其它值之間切換,雖說功能可以實作,但是效果略顯生硬,是以會有這樣的需求——希望元素展開收起能具有平滑的效果。

實作

max-height

首先想到的是通過

height

auto

之間切換,但是結果可能并不會是我們所預期的那樣,原因是我們将要展開的元素内容是動态的,即高度值不确定,是以

height

使用的值是預設的

auto

,從

0px

auto

是無法計算的,是以無法形成過渡或動畫效果。

據此我們可以使用

max-height

,将

max-height

過渡到一個能夠大于完全顯示内部元素的值,展開後的max-height值,隻需要設定為保證比展開内容高度大的值即可,在

max-height

值比

height

值大的情況下,元素仍會預設采用自身的高度值即

auto

,如此一來一個高度不定的元素展開收起動畫效果就實作了。

請注意這種方式實作還是有限制的,使用

CSS

進行過渡動畫的時候依舊是通過計算

到設定的

max-height

高度值進行計算的,在實際應用中如果

max-height

值太大,在元素收起的時候将會産生延遲的效果,這是因為在收起時,

max-height

從設定的特别大的值,到元素自身高度值的變化過程将占用較多時間,此時畫面表現則會産生延遲的效果。是以建議将

max-height

值設定為足夠安全的最小值,這樣在收起時即使有略微延遲,也會因為時間很短,難以被使用者感覺,将不會影響體驗。

<!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>
           

height

使用

max-height

必定有一定的局限性,那麼不如我們在

DOM

加載完成後就取得元素的實際高度并儲存,之後直接利用這個真實高度與

進行動畫過渡即可,因為浏覽器的渲染順序,在解析

JavaScript

時會阻塞

DOM

的渲染,是以在擷取元素實際高度再設定高度為

的過程中一般不會出現閃爍的情況,如果實在擔心因為擷取高度之後再将高度設定為

可能會有一個閃爍的過程,那麼我們可以取得元素父節點後調用

cloneNode(true)

方法或者

innerHTML

方法取得字元串再

innerHTML

到一個新建立的節點,目的就是将其拷貝,之後将其使用絕對定位等放置到螢幕外即将其設定到螢幕能夠顯示的外部區域,注意此時要設定

body

overflow: hidden;

,之後利用

getComputedStyle

取得實際高度,然後再将其移出

DOM

結構,此時有了實際高度就可以進行動畫過渡了,下面簡單的實作一下在

DOM

加載時便取得實際高度進行動畫實作。

<!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>

           

translateY

還有一種常用實作動畫的方式,即首先将外層元素沒有動畫過渡的形式直接展開,再将選項加入動畫緩慢下落,通常利用

transform: translateY();

去實作這個緩慢下降的動畫,在微信的

WEUI

小程式元件庫的首頁就是采用這種實作方式。

<!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