CSS transition delay簡介與進階應用

背景

在平常的項目開發中,咱們會很常常的碰見以下的需求:javascript

  • 在瀏覽器頁面中,當鼠標移動到某個部分後,另外一個部分在延遲若干時間後出現
  • 在鼠標移除該區域後,另外一部分也在延遲若干時間後消失

我相信這是一個很常見的一個需求,有不少種方式可以實現,可是,其實現方式的原理各不相同,也有利有弊。css

實現方案

CSS

在CSS中,有一個僞類hover也可以監聽鼠標移動到某個元素上面,所以咱們也能夠利用CSS來實現咱們剛剛的功能。html

咱們須要使用的是CSS3中的新特性:transitionjava

transition是一個簡寫屬性,可設置transition-property, transition-duration, transition-timing-function, transition-delaytransition用來定義元素兩種狀態之間的過渡。不一樣狀態能夠用 pseudo-classes 定義,好比 :hover:active或使用JavaScript設置。瀏覽器

該屬性第一個值爲須要變化的屬性值,第二個值爲其持續時間,第三個值爲變化方式,第四個值爲其延時。該屬性指定的值只要指定的屬性有任何變化,都會觸發該屬性。即在從該樣式到其餘樣式,以及其餘樣式回到該樣式時都會產生效果。例如:編輯器

transition:opacity 1s linear 1s;
複製代碼

具體介紹請看MDN官方介紹post

如今,讓咱們用transition屬性來實現上面的功能。簡單點以下所示:動畫

//HTML文件
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="css/index.css">
</head>

<body>
    <div id="container">
        <div id="div1"></div>
        <div id="div2"></div>
    </div>
</body>

</html>
複製代碼
//Sass文件
#container {
  display: inline-block;
  #div1 {
    width: 100px;
    height: 100px;
    background-color: #000;
  }

  #div2 {
    visibility: hidden;
    opacity: 0;
    width: 100px;
    height: 100px;
    background-color: #F00;
    transition-delay: 0.5s;
  }
  &:hover {
    #div2 {
      visibility: visible;
      opacity: 1;
    }
  }
}
複製代碼

經過在CSS中添加transition-duration,咱們能夠實現延時顯示的目的。而且,咱們不須要本身去清除定時器,而由瀏覽器來判別。ui

在此,咱們爲何不用display屬性呢,由於在display改變時,transition並不會生效。spa

那咱們爲何須要在使用了opacity屬性的時候同時使用visibility屬性呢。由於opacity屬性只是讓元素變得透明,而不會讓元素消失。若是不加速visibility屬性的話,那元素變透明後仍然能夠點擊,那麼會出現一些奇怪的影響。

到目前爲止,咱們利用CSS徹底模擬了第一部分咱們使用JavaScript實現的功能,並且看上去更簡潔。那麼,下面咱們來說一些更加複雜的功能有助於你們理解transition

Transition進階

如今咱們須要在鼠標移動上去後,出現一個漸變的效果,讓另外一框慢慢出現,同時在鼠標移出的時候也有漸變消失的效果,那麼咱們就須要來使用一下transition的另外幾個屬性。

在上面的代碼中稍加改動,就可以獲得咱們須要的效果

transition: opacity 0.5s linear;
複製代碼

這樣的話,在鼠標移入的時候,會有一個漸變的效果。可是,問題也出現了,在鼠標移出的時候,div2立馬就消失了。讓咱們來分析一下鼠標移入和移出時的效果。

當鼠標移入時:

  1. 鼠標移入div1元素
  2. 觸發了hover事件,div2的visibility屬性變爲visible
  3. transition屬性讓opacity屬性從0變爲1

而當鼠標移出時:

  1. 鼠標移出div1元素
  2. hover事件中止觸發,div2的visibility屬性變爲hidden
  3. transition屬性讓opacity屬性從1變爲0

根據上面的狀況咱們知道,當hover事件結束後,visibility屬性立馬就變成了hidden了,所以後面的動畫效果就沒法看到了。

那麼若是咱們爲visibility屬性也加上延時呢,能不可以達到咱們的目標,讓咱們來看一下效果

transition: visibility 0s linear 0.5s, opacity 0.5s linear;
複製代碼

代碼改動爲如上狀況後,咱們會發現,當鼠標移出的時候,可以看到動畫效果。可是當鼠標移入時,動畫效果消失了,如今再讓咱們來分析下爲何會出現這個狀況。

當時鼠標移入時:

  1. 鼠標移入div1元素
  2. 觸發hover事件
  3. transition屬性讓opacity屬性從0變爲1
  4. visibility屬性變爲visible

當鼠標移出時:

  1. 鼠標移出div1元素
  2. hover事件中止觸發
  3. transition屬性讓opacity屬性從1變爲0
  4. visibility屬性變爲hidden

從上面的分析咱們能夠知道,由於visibility屬性爲不連續變化屬性,所以在transition中只有transition-delay對該屬性產生了效果。因此visibility屬性延時了0.5s執行,致使了在鼠標移入時看不到效果。

那麼,咱們有沒有辦法同時在鼠標移入和移出的時候同時看到動畫效果呢。須要達到這個目的,其實換一個思路立馬就可以解決。咱們不僅須要在hover事件中重置這個延時,將其從新指定爲0,立刻就可以達到咱們想要的效果。具體示例代碼以下:

#container {
    display: inline-block;
    #div1 {
      width: 100px;
      height: 100px;
      background-color: #000;
    }
  
    #div2 {
      visibility: hidden;
      opacity: 0;
      width: 100px;
      height: 100px;
      background-color: #F00;
      transition: visibility 0s linear 0.5s, opacity 0.5s linear;
    }
    &:hover {
      #div2 {
        visibility: visible;
        opacity: 1;
        transition-delay: 0s;
      }
    }
  }
複製代碼

那麼如今讓咱們來分析下爲何這麼寫可以達到咱們須要的效果。

當鼠標移入時:

  1. 鼠標移入div1
  2. hover事件觸發,從新指定transition屬性的transition-delay爲0s,visibility屬性由hidden立馬變成visible
  3. transition屬性讓opacity屬性由0變爲1

當鼠標移出時:

  1. 鼠標移出div1
  2. hover事件中止觸發,transition延時恢復到0.5s,所以visibility屬性不會立刻觸發
  3. transition屬性讓opacity屬性由1變爲0
  4. visibility屬性由visible變爲hidden

咱們經過一個很簡單巧妙的方法,經過改變transition-delay,從而讓visibility屬性當即執行,達到了咱們須要的效果。

JavaScript

附上利用JS來實現該方案的代碼用於參考。這個其實應該是大部分人會想到的方法,利用mouseover或者click事件來進行事件的監聽,利用setTimeout來進行延時處理,例如這樣

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style> #div1 { width: 100px; height: 100px; background-color: #000; } #div2 { display: none; width: 100px; height: 100px; background-color: #F00; } </style>
</head>

<body>
    <div id="div1"></div>
    <div id="div2"></div>
    <script> document.addEventListener('DOMContentLoaded', function () { var div1 = document.getElementById('div1'); var div2 = document.getElementById('div2'); div1.addEventListener('mouseover', function () { setTimeout(function () { div2.style.display = 'block'; }, 500); }); div1.addEventListener('mouseleave', function () { setTimeout(function () { div2.style.display = 'none'; }, 500); }); }); </script>
</body>

</html>
複製代碼

可是上面這個代碼有一個比較嚴重的問題,就是當鼠標兩次移動上去的間隔小於500ms時,上一次的setTimeout的代碼仍是會觸發,所以會看到一次閃動的效果。

所以,咱們須要在檢測到兩次間隔小於500ms時,清除掉上次的setTimeout的代碼。因此,改動代碼以下(注:若有表現與描述不一致請看文章末尾說明):

div1.addEventListener('mouseover', function () {
    clearTimeout(handle);
    setTimeout(function () {
        div2.style.display = 'block';
    }, 500);
});

var handle = 0;
div1.addEventListener('mouseleave', function () {
    handle = setTimeout(function () {
        div2.style.display = 'none';
    }, 500);
});
複製代碼

調整爲上述代碼以後,基本能夠知足咱們的需求。後續若是須要添加動畫之類的操做,也只須要繼續像代碼添加相關邏輯便可,在此就再也不演示。

總結

在需求開發的過程當中,遇到了這個問題。最開始用JavaScript實現,開發起來比較複雜,容易與業務邏輯代碼混在一塊兒很差維護。經過CSS來實現這個功能,既簡單高效,又可以避免增長JavaScript複雜度,是一個比較優的解決方案。

參考

說明

jsbin是一個在線的編輯器,可以在代碼編寫完成後立刻看到效果,可是該文中有部分代碼在jsbin中出現表現與本地不一致的狀況(例如CSS進階中最後一個代碼),你們能夠將代碼放到本地驗證。因爲代碼效果時好時壞,猜想可能與jsbin的容器相關。

相關文章
相關標籤/搜索