關於CSS Transition,你須要知道的事

CSS3的過渡屬性,給web應用帶來了簡單優雅的動畫,可是比起初次相見,他(transition)有許多細則。javascript

在這片文章中,我將會專研CSS3的過渡(transition)中更加複雜的部分,從鏈式和事件到硬件加速和動畫函數。css

讓瀏覽器控制動畫序列,經過改變幀率,減小繪畫和減小GPU的工做,可以優化性能和效率。java

應用 transition

一個最簡單使用transition的方法就是和CSS僞元素一塊兒用,好比:hover。注意咱們在指定屬性名字,transition的時長,以及默計時函數,linear。git

.element {
  height: 100px;
  transition: height 2s linear;
}

.element:hover {
  height: 200px;
}

當:hover僞元素被激活的時候,這高度會動態地在兩秒內從100px過分到200px。github

duration是惟一在transition縮寫中須要的項目。瀏覽器默認的定時方法是ease,以及一個all的屬性,除非他們已經提供了。web

當談論到激活transition,咱們不但願被限制於使用僞元素 —— 很顯然這不靈活。解決這個的方法就是用程序添加和刪除class編程

/* CSS */
.element {
  opacity: 0.0;
  transform: scale(0.95) translate3d(0,100%,0);
  transition: transform 400ms ease, opacity 400ms ease;
}

.element.active {
  opacity: 1.0;
  transform: scale(1.0) translate3d(0,0,0);
}

.element.inactive {
  opacity: 0.0;
  transform: scale(1) translate3d(0,0,0);
}

// JS with jQuery
var active = function(){
  $('.element').removeClass('inactive').addClass('active');
};

var inactive = function(){
  $('.element').removeClass('active').addClass('inactive');
};

以上的列子,咱們用了2個不一樣的過渡(transition),當激活的時候,元素向上滑動,當無效化以後,淡出。全部的javascript所作的事就是切換active 和 inactive這兩個class。api

過渡漸變

不是全部的CSS屬性都能過渡,最基本的規則是你只能過渡絕對值。好比,你不能讓height從 0px過渡到auto,瀏覽器不能計算中間過分值,所以屬性變化是瞬間的。Oli Studholme提供了便利的 一份徹底過分屬性的列表瀏覽器

同時,有一些很好的解決方法。第一個方法包括添加透明度到漸變,而後過渡到背景色。好比:函數

.panel {
  background-color: #000;
  background-image: linear-gradient(rgba(255, 255, 0, 0.4), #FAFAFA);
  transition: background-color 400ms ease;
}

.panel:hover {
  background-color: #DDD;
}

若是漸變是持續的,你能夠過渡background-position,就像這裏寫的,不然,你的最後手段是建立兩個元素,一個放在另外一個之上,而後過渡你的透明度。

.element {  
  width: 100px;  
  height: 100px;  
  position: relative;
  background: linear-gradient(#C7D3DC,#5B798E);    
}  

.element .inner { 
  content: '';
  position: absolute;
  left: 0; top: 0; right: 0; bottom: 0;
  background: linear-gradient(#DDD, #FAFAFA);          
  opacity: 0;
  transition: opacity 1s linear;
}

.element:hover .inner {
  opacity: 1;
}

後者方法的須要注意的是,這須要額外的標記,而且在內部的div可以捕捉到指針事件。僞元素,相似:before和:after能夠是過分理想的使用案例。

硬件加速

過渡某個屬性,好比left和margin會致使瀏覽器每幀都會從新計算樣式。這消耗至關昂貴,而且可能會致使沒必要要的重繪,特別是若是你在屏幕上有不少元素。這在低性能設備上顯得特別明顯,好比手機。

這個解決方案是使用CSS過渡來減小渲染給GPU帶來的壓力。簡單來講,這在過渡的時候,將元素變成了一張圖片,避免任何樣式從新計算,這極大程度上增長了性能。一個簡單強迫瀏覽器用硬件渲染一個元素的方法是,設置轉型的Z軸,這個你能夠用translate3d:

transform: translate3d(0,0,0);

不過這不是根治性能的方法,而且會帶來許多自己的問題。只有當須要的時候,你才應該用硬件加速,而且徹底不須要在每一個元素上都使用它。

好比,硬件加速會致使微妙的字體問題,好比一個字體出現的時候失去了加粗效果。這是由於一個bug,當元素開啓硬件加速的時候,不支持子像素抗鋸齒。你能夠看到在兩個渲染模式下的一個清晰的差異。
圖片描述

此外,不一樣的瀏覽器用不一樣的硬件加速的庫,這可能會形成跨瀏覽器問題。好比,當Chrome和Safari都是WebKit內核的,Chrome使用Skia來作圖形渲染,然而同時Safari用的CoreGraphics。這兩個庫之間的差異是細微的,可是確是存在。

你能夠用Chrome的開發者工具概覽頁面,顯示全部的重繪。外加你能夠在開發者工具選項中選擇顯示三角形,甚至能夠經過 about:flags 打開復合渲染層邊界,來看哪一個層是做用在GPU上的。關鍵是批量在DOM刷新下,減小繪畫,而且從GPU上減小最多的的壓力。
圖片描述

若是你在瀏覽器之間由於硬件加速而有了顯示問題,好比閃爍或者顫動,確保你沒有用transform3d()的CSS屬性在元素上。

剪裁

爲了充分利用GPU渲染,你須要避免使用CSS樣式變換而不是像width這種從新計算樣式的屬性。若果你確實須要給元素的寬度作動畫?解決方案就是剪裁。

在如下的例子中個,你能夠看到一個搜索框和2個過分狀態。第二個擴展狀態被一個剪裁的元素給隱藏了。
圖片描述

過渡這個擴展的寬度,咱們所須要作的就是轉變X軸到左邊。這邊的關鍵是咱們用translate3d而不是改變元素的寬度。

.clipped {
  overflow: hidden;
  position: relative;
}

.clipped .clip {
  right: 0px;
  width: 45px;
  height: 45px;
  background: url(/images/clip.png) no-repeat
}

input:focus {
  -webkit-transform: translate3d(-50px, 0, 0);
}

確保咱們不會在每一幀從新計算元素的寬度,過渡會變得順滑和高性能。

時間函數

到目前爲止,咱們用了一些瀏覽器預約義時間函數linear, ease, ease-in, ease-out和ease-in-out。對於更多複雜的時間函數來講,咱們要寫咱們本身的時間函數,經過定義貝塞爾曲線的4個關鍵點。

transition: -webkit-transform 1s cubic-bezier(.17,.67,.69,1.33);

圖片描述

規劃過渡

在CSS中寫過渡很是好,可是有時候你須要更多控制,特別是談到鏈式過渡的時候。幸運的是咱們不只能從javascript中調用過渡,也能定義他們。

CSS過渡有一個魔法般的all屬性,這確保了任何屬性改變都是過渡的。讓咱們看看如何實踐他們

var defaults = {
  duration: 400,
  easing: ''
};

$.fn.transition = function (properties, options) {
  options = $.extend({}, defaults, options);
  properties['webkitTransition'] = 'all ' + options.duration + 'ms ' + options.easing;
  $(this).css(properties);
};

如今咱們有個jQuery函數$.fn.transition,咱們能夠用它來編程調用過渡。

$('.element').transition({background: 'red'});

過渡回調

接下來的步奏是鏈式過渡,是過渡結束後回調。你能夠在Webkit中得到這個狀態,經過監聽webkitTransitionEnd這個事件。

var callback = function () {
    // ...
  }

  $(this).one('webkitTransitionEnd', callback)
  $(this).css(properties);

記住有時候事件沒有綁定,常常是在那些屬性不改變或者一個繪畫沒有被觸發的狀況下。爲了確保咱們一直有一個回調,讓咱們設置一個超時,這會手動幫咱們觸發時間。

$.fn.emulateTransitionEnd = function(duration) {
  var called = false, $el = this;
  $(this).one('webkitTransitionEnd', function() { called = true; });
  var callback = function() { if (!called) $($el).trigger('webkitTransitionEnd'); };
  setTimeout(callback, duration);
};

咱們在設置元素css以前,請求 $.fn.emulateTransitionEnd(),以確保咱們過渡以後會有CSS回調。

$(this).one('webkitTransitionEnd', callback);
$(this).emulateTransitionEnd(options.duration + 50);
$(this).css(properties);

鏈式過渡

所以,如今咱們可以經過寫程引用過渡,當他們結束以後得到回調,咱們可以開始排序過渡。咱們可以寫本身的序列來作這件事,可是咱們用了jQuery,咱們最好滲透庫的現存方法。

jQuery提供了2個關鍵函數和他的隊列和API聯繫,$.fn.queue(callback)和 $.fn.dequeue()。前者加了一個回調到隊列中,而後後者執行下一個隊列中的項目。

換句話說,咱們須要設置咱們的CSS過渡在$.fn.queue回調之中,而後確保當過渡完成的時候,咱們調用了$.fn.dequeue

var $el = $(this);
$el.queue(function(){
  $el.one('webkitTransitionEnd', function(){
    $el.dequeue();
  });
  $el.css(properties);
});

這個例子相對簡單,可是他讓咱們建立了複雜的鏈式動畫,而且甚至使用jQuery的delay()函數:好比:

$('.element').transition({left: '20px'})
             .delay(200)
             .transition({background: 'red'});

從新繪製

在過渡的時候,你會須要兩組CSS屬性。最初的屬性是動畫應該從哪裏開始,和最後的屬性,過渡應該在哪裏結束。

$('.element').css({left: '10px'})
             .transition({left: '20px'});

然而,你會發現若是你應用了兩組屬性,一個立馬在另外一個以後運行了,而後瀏覽器嘗試優化屬性改變,無視你的初始屬性和阻止過渡。在場景以後,瀏覽器繪製以前,批處理屬性變更,常常會加速渲染,常常會有不良反應。

解決方法是在兩組屬性之間強迫重繪。一個簡單的方法是獲取Dom元素的offsetHeight屬性,就像這樣

$.fn.redraw = function(){
  $(this).each(function(){
    var redraw = this.offsetHeight;
  });
};

這在素有的瀏覽器中有有效,可是我有次很巧合地在Android中,這依然不行。可供替代的方法有timeout或者切換class名。

$('.element').css({left: '10px'})
             .redraw()
             .transition({left: '20px'});

將來

過渡(Transition)正在活躍地應用,以及下一個標準看上去頗有前景。這個建議包括了一個新的javascript的API,這個api專一於過渡現存的限制,並給與開發者更多的靈活性。

實際上,你能夠在github上找到新API的鋪墊。這包括了舉例一個Animation構造函數,傳遞到一個元素,讓它作動畫,作動畫的屬性,還有其餘的屬性,諸如延遲。

var anim = new Animation(elem, { left: '100px' }, 3);
anim.play();

有了這個新的API,你能夠同步動畫,提供私人定製的時間函數,而且或者完整的回調。這真是一件激動人心的事啊!
圖片描述

相關文章
相關標籤/搜索