display中的transition實現

display中的transition實現

這裏討論的transition,是針對於display:none/block;下的實現或替代性方案。

因爲過渡是基於數值和時間來計算的,比如長度、顏色、角度等屬性值,是可以在單位時間內變化一定數值,從而達到過渡的動畫效果。

像我們常見的opacity/width/height/margin/padding等屬性,都是可以應用過渡效果的,然而display這個屬性比較尷尬,兩種狀態,要麼顯示,要麼隱藏,因此無法應用transition效果。

下面探究一下如何變相地實現顯隱過渡效果。


1. 原生js實現顯隱transition

方案一:visibility+opacity

對於display屬性無法應用過渡的問題,可以使用visibility來代替。

雖然visibility也是兩種狀態hiddenvisible,但這個屬性比較特別,hidden相當於0,visible相當於1,從hiddenvisible的轉化是可以過渡的,只是並不會有動畫效果。

也就是說,設置了transition: visibility 1s linear;,當其visibility值變爲hidden時,元素並不會馬上消失,而是經過1s後,突然消失,也不會有漸隱的動畫效果。

因此,我們需要配合opacity來做到漸隱漸顯的過渡效果。

<button id="btn">點擊顯隱</button>  
<div id="box" class=""><div>

<style>
    #box{
        width: 100px;
        height: 100px;
        background-color: green;
        visibility: visible;
        opacity: 1;
        transition: all 1s linear;
    }
    .hide{
        opacity: 0;
        visibility: hidden;
    }
</style>

<script>
    var box = document.getElementById("box");
    var btn = document.getElementById("btn");
    btn.onclick = function(){
        if(box.className.indexOf("hide") != -1){
            box.className = "";
        }else{
            box.className += "hide";
        }
    };
</script>

兩個問題:

  1. 雖然單獨使用opacity也可以達到效果,但是這樣的盒子,不能透過它去觸發下面元素的點擊事件,即使它看不見了,也會造成阻礙。而設置了visibilityhidden,就不影響其他元素的點擊事件。
  2. 使用這種方案,並不能真正的隱藏元素,依舊會在文檔流中佔據位置,在頁面上留下一塊空白。並且visibility屬性在IE9及以下的瀏覽器不支持。


方案二:timeout

前面提到display屬性不支持過渡,那麼我們可以讓其他可見的屬性過渡,並與display值的變化分開執行,看起來就像display過渡了一樣。

需要注意:

box.style.opacity = 0;
box.style.display = "none";

這樣不可行,因爲瀏覽器的執行機制,兩步其實會合並執行,元素會直接從文檔流中移除,因此根本看不到透明度的過渡變化。

我們可以使用定時器,強制拆開這兩步。

<button id="btn">點擊顯隱</button>  
<div id="box" class=""><div>

<style>
    #box{
        width: 100px;
        height: 100px;
        background-color: green;
        opacity: 1;
        transition: all 1s linear;
    }
</style>

<script>
    var box = document.getElementById("box");
    var btn = document.getElementById("btn");
    btn.onclick = function(){
        if(getComputedStyle(box,null)["display"] == "block"){
            box.style.opacity = 0;
            setTimeout(function(){
                box.style.display = "none";
            },1000);//這裏因爲要等待過渡的1s,然後才消失
        }else{
            setTimeout(function(){
                box.style.display = "block";
            });//因爲緊接着要過渡,所以delay爲0
            box.style.opacity = 1;
        }
    };
</script>


方案三:transitionend

transition動畫結束時,會觸發一個事件,叫做transitionend,我們可以用它替換定時器。

box.style.opacity = 0;
box.addEventListener("transitionend", function(e){
    //propertyName是觸發事件相關聯的屬性,詳見MDN
    if(e.propertyName == "opacity"){
        box.style.display = "none";
    }
});



2. jQuery的顯隱transition

在jQuery中做到這個過渡效果非常簡單,使用animate即可,內部已經自動給我們處理好了。

//toggle是控制顯隱的按鈕
//form是需要顯隱的元素
$('.toggle').click(function(){
    $('.form').animate({
        //'padding-top': 'toggle',
        //'padding-bottom': 'toggle',
        opacity: "toggle",
        height: "toggle" 
    }, 'slow');
});

不需要我們主動判斷display的值,只要使用了toggle作爲動畫效果值,jQuery會自動切換showhide,並且這裏還可以看到透明度,高度同時變化。

如果是兩個元素連續排列,初始display狀態分別爲blocknone,只需要給它們設置類名都爲form,當點擊按鈕時,會交替顯示元素,並且切換的過渡效果相當自然,常用來做登錄和註冊的表單顯示切換。



3. 框架內部的顯隱transition

其實框架內部的過渡動畫效果,底層都是用CSS3中transitionanimation實現的。


3.1 Vue的過渡

Vue2.0的過渡是一個內置組件transition元素會把過渡效果應用到其包裹的內容上,而不會額外渲染DOM元素。

使用方式:

data:{show:true}

<input type="button" value="點擊隱藏顯示" @click="show=!show">

<transition name='fade'>
    <div v-show='show'></div>
</transition>

transition指定namefade後,將會自動生成幾個過渡狀態的class:

  • .fade-enter{} —— 出現的初始狀態
  • .fade-enter-active{} —— 當元素進入,變化過渡的趨向狀態
  • .fade-leave{} —— 離開的初始狀態(通常不使用)
  • .fade-leave-active{} —— 當元素離開,變化過渡的趨向狀態

一般將過渡效果transition設置給active:

.fade-enter-active,.fade-leave-active{
    transition: all .5s linear;
}

.fade-enter{
    opacity: 0;
}

vue過渡狀態

當然,我們也可在Vue中使用第三方css動畫效果庫。

詳見Vue2.0過渡及其鉤子函數



3.2 Angular的過渡

這裏使用的版本是Angular1,用到了angular-animate.js插件。

<html ng-app="myapp">
    <body ng-controller="test" ng-init="showFlag = false">
        <button id="btn">點擊顯隱</button>  
        <div id="box" class="myanimate" ng-show="showFlag"><div>
    </body>
</html>    

<style>
    #box{
        width: 100px;
        height: 100px;
        background-color: green;
    }
    .myanimate.ng-hide-add,
    .myanimate.ng-hide-remove {
        transition: all linear 0.5s;
    }
    .myanimate.ng-hide{
        opacity: 0;
    }

</style>

<script src="https://cdn.bootcss.com/angular.js/1.7.0/angular.js"></script>
<script src="https://cdn.bootcss.com/angular.js/1.7.0/angular-animate.js"></script>
<script>
    angular.module("myapp",['ngAnimate']).controller("test",function($scope){});
</script>

因爲angular-animate插件,在元素向ng-hide的狀態切換時,會給該元素自動添加兩個class:ng-hide-add/ng-hide-remove,分別代表向ng-hide轉換和從ng-hide變爲其他狀態。

所以我們可以給這兩個class設置transition,再給ng-hide設置一個最終的消失狀態,這樣就有了過渡效果。與上面Vue中的例子類似。

myanimate是我自己加的class,使用了交集選擇器,是爲了精確定位需要應用過渡效果的元素。

個人感覺Angular中的過渡變化相對於Vue來說比較複雜,不同的指令還有不同的animation class類,需要對照文檔來使用。

ng-animate
詳細介紹見官方文檔

我們也可以使用Angular中現成的第三方css動畫效果庫:ngAnimate.css,通過增刪class很方便地控制過渡效果。


3.3 collapse效果實現

有時候有這樣的需求,類似collapse的過渡效果,當點擊按鈕,下拉選項框會往上收攏消失或向下擴張顯示,我們用display的過渡,只能實現透明度的變化,而不能做到高度的過渡效果,因爲display:none;的元素寬高都是auto

transition: all 1s linear;的結果就是,經過1秒,元素的透明度由1變爲0,緊接着突然消失,然後下面的元素盒子由於有了空間,迅速頂上來,讓人覺得很突兀。

我想到的有兩種純css的實現方案:

  1. 正常的設置過渡效果,然後給該過渡元素設置浮動,當元素顯示時,下面的盒子需要設置margin-top爲該元素的高度,元素隱藏時,給下面盒子移除掉margin-top屬性,同時給下面盒子也設置過渡,這樣相當於用下面盒子幫助實現了collapse的效果。

    <div class="form myanimate" ng-show="showFlag"></div>
    <div class="table" ng-class="{'mt':showFlag}" style="width: 500px;height: 300px;background-color: green;"></div>
    
    <style>
    .myanimate.ng-hide-add,
    .myanimate.ng-hide-remove {
    transition: all linear 0.5s;
    }
    .myanimate.ng-hide{
    opacity: 0;
    }
    
    .form{
       float: left;
    }
    .table{
       transition: all 0.5s linear;
       /*提高層級,以免被浮動遮住*/
       position: relative;
    }
    .mt{
       margin-top: 96px;
    }
    </style>

  2. 捨棄ng-animate的過渡效果,直接添加class類,當ng-hide狀態時,讓元素高度變爲0,並且設置transition,簡單粗暴有效。

    <div class="form" ng-show="showFlag" ng-class="{'end': !showFlag}">
    
    
    <style>
    .form{
       transition: all linear 0.5s;
       max-height: 96px; /*稍微大一點也沒事*/
       overflow: hidden;
       opacity: 1;
    }
    .end{
       max-height: 0;
       opacity: 0;
    }
    </style>

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