display中的transition實現
這裏討論的transition
,是針對於display:none/block;
下的實現或替代性方案。
因爲過渡是基於數值和時間來計算的,比如長度、顏色、角度等屬性值,是可以在單位時間內變化一定數值,從而達到過渡的動畫效果。
像我們常見的opacity/width/height/margin/padding
等屬性,都是可以應用過渡效果的,然而display
這個屬性比較尷尬,兩種狀態,要麼顯示,要麼隱藏,因此無法應用transition
效果。
下面探究一下如何變相地實現顯隱過渡效果。
1. 原生js實現顯隱transition
方案一:visibility+opacity
對於display
屬性無法應用過渡的問題,可以使用visibility
來代替。
雖然visibility
也是兩種狀態hidden
和visible
,但這個屬性比較特別,hidden
相當於0,visible
相當於1,從hidden
到visible
的轉化是可以過渡的,只是並不會有動畫效果。
也就是說,設置了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>
兩個問題:
- 雖然單獨使用
opacity
也可以達到效果,但是這樣的盒子,不能透過它去觸發下面元素的點擊事件,即使它看不見了,也會造成阻礙。而設置了visibility
爲hidden
,就不影響其他元素的點擊事件。 - 使用這種方案,並不能真正的隱藏元素,依舊會在文檔流中佔據位置,在頁面上留下一塊空白。並且
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
會自動切換show
和hide
,並且這裏還可以看到透明度,高度同時變化。
如果是兩個元素連續排列,初始display
狀態分別爲block
和none
,只需要給它們設置類名都爲form
,當點擊按鈕時,會交替顯示元素,並且切換的過渡效果相當自然,常用來做登錄和註冊的表單顯示切換。
3. 框架內部的顯隱transition
其實框架內部的過渡動畫效果,底層都是用CSS3中transition
和animation
實現的。
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
指定name
爲fade
後,將會自動生成幾個過渡狀態的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中使用第三方css動畫效果庫。
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
類,需要對照文檔來使用。
詳細介紹見官方文檔。
我們也可以使用Angular中現成的第三方css動畫效果庫:ngAnimate.css,通過增刪class很方便地控制過渡效果。
3.3 collapse效果實現
有時候有這樣的需求,類似collapse
的過渡效果,當點擊按鈕,下拉選項框會往上收攏消失或向下擴張顯示,我們用display
的過渡,只能實現透明度的變化,而不能做到高度的過渡效果,因爲display:none;
的元素寬高都是auto
。
transition: all 1s linear;
的結果就是,經過1秒,元素的透明度由1變爲0,緊接着突然消失,然後下面的元素盒子由於有了空間,迅速頂上來,讓人覺得很突兀。
我想到的有兩種純css的實現方案:
正常的設置過渡效果,然後給該過渡元素設置浮動,當元素顯示時,下面的盒子需要設置
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>
捨棄
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>