轉(zhuǎn)自:http://www.itdecent.cn/p/56f8ddafc63f
上一篇介紹了CSS3里transform變形屬性,本篇介紹另一個人氣動畫屬性transition。transition從效果上看是一種平滑過渡的動畫,本質(zhì)上是在線性時間內(nèi)將屬性從開始值過渡到結(jié)束值。例如獲得焦點,點擊鼠標等動作導致CSS屬性值的變化是瞬間完成的,感覺有點生硬。用transition可以指定在某時間段內(nèi)將屬性值平滑過渡,增強用戶體驗。
4個子屬性
transition-property
transition-duration
transition-timing-function
transition-delay
觸發(fā)過渡的方式
transitionend事件
隱式過渡
開關(guān)過渡和永久過渡
auto過渡
先實際感受一下,你可以點擊這里
4個子屬性
transition有4個子屬性:transition-property,transition-duration,transition-timing-function,transition-delay
transition-property指定需要過渡的CSS屬性。并不是所有屬性都能過渡的,只有能數(shù)字量化的CSS屬性才能過渡。哪些屬于能數(shù)字量化的CSS屬性呢?例如:
顏色系,如color,background-color,border-color,outline-color等
數(shù)字系,實在太多了,如width,height,top,right,bottom,left,zoom,opacity,line-height,background-position,word-spacing,font-weight,vertical-align,outline-outset,z-index等。
01系,如visibility(0表示隱藏,1表示顯示)
還有更多如漸變,陰影等分類請參照W3C的Animation of property types,不一一列舉了。W3C上還有可過渡屬性一覽表Properties from CSS。通常只要能設(shè)數(shù)字(包括%百分比)的屬性都能過渡。
除了單個指定屬性外,transition-property還能設(shè)為all,表示所有屬性都將獲得過渡效果。
transition-duration過渡需要的時間,單位可指定s秒,也可指定ms毫秒。默認值是0,表示立刻變化。如果設(shè)置了多個過渡屬性,但每個屬性的過渡時間都一樣,你沒必要為transition-duration設(shè)多個值,只有設(shè)一個即可,該值會應用到所有過渡屬性上。
transition-timing-function過渡函數(shù),有linear,ease,ease-in,ease-out,ease-in-out,cubic-bezier(n,n,n,n),steps。其實它們都是貝賽爾曲線。如下

看貝賽爾曲線就知道了,linear是勻速過渡,ease是先快再慢的節(jié)奏,ease-in是加速沖刺的節(jié)奏,ease-out是減速到停止的節(jié)奏,ease-in-out是先加速后減速的節(jié)奏。
現(xiàn)在動畫的精度越來越高,如果預定義好的這些函數(shù)滿足不了你的需求,可以通過cubic-bezier(n,n,n,n)自定義平滑曲線。從上面的圖形中觀察到,貝塞爾曲線有4個點,左下為起始點P0坐標固定為(0,0),右上為終點P3坐標固定為(1,1),中間有兩點P1和P2的坐標就是cubic-bezier(n,n,n,n)的參數(shù)。通過4條連起來的直線,生成平滑的曲線。一圖勝千言:

如果要憑腦子空寫出貝賽爾函數(shù)的代碼,可能比較困難。好在不用你自己去計算,可以到工具網(wǎng)站(如貝賽爾立方)上自動生成想要效果的代碼。你也可以在該站點上,體驗一把linear,ease,ease-in,ease-out,ease-in-out間的差異。
steps可以把過渡階段分割成等價的幾步。什么意思呢?一圖勝千言:

steps函數(shù)有兩個參數(shù),第一個參數(shù)是分割的數(shù)量,第二個參數(shù)可選,是關(guān)鍵字start,end,不設(shè)的話默認是end。因此steps(4, start)等價于step-start(4),steps(4, end)等價于step-end(4)。例如steps(4, end)并非像貝塞爾曲線那樣平滑過渡,相當于將過渡過程從頭至尾分成4步,在每一步瞬間完成過渡。最上面的例子中已經(jīng)有所展示,很容易理解
transition-delay延遲開始過渡的時間,默認值是0,表示不延遲,立即開始過渡動作。單位是s秒或ms毫秒。w3cschool上沒說的是,該屬性還能設(shè)負時間,意思是讓過渡動作從該時間點開始啟動,之前的過渡動作不顯示。不信你可以試試。負延遲時間在animation動畫里確實有用,但恕我才疏學淺,在transition里實在不知道有什么卵用。
你可以單獨指定這4個子屬性,也可以像background等屬性一樣,合并在transition屬性里指定。但合并時要注意,因為有transition-duration和transition-delay都是時間,瀏覽器會根據(jù)先后順序,將第一個時間認作為transition-duration,第二個時間認作為transition-delay。
是分開或者合并指定并無標準答案。分開指定可能代碼易讀性高一點。但當頁面需要適應各瀏覽器時,每個都要加上-ms-,-moz-等前綴,代碼會變的很多,合并在一起代碼稍微少點。另外也可以同時指定多個過渡效果,例如transition: background 1s linear 2s, border-radius 2s ease-in 3s;。
觸發(fā)過渡的方式
常見的就是偽類觸發(fā):hover,:focus,:active,:checked等。還有例如@media媒體查詢,根據(jù)設(shè)備大小,橫屏豎屏切換時觸發(fā)。還有如click,keydown等JS事件觸發(fā)。頁面加載也能觸發(fā)就不一一列舉了??傊^渡的本質(zhì)是在時間段內(nèi)平滑過渡屬性值,與怎么觸發(fā)沒有關(guān)系。
transitionend事件
transition既然涉及時間,自然就有事件。參照MDN有transitionend事件,顧名思義就是過渡結(jié)束時觸發(fā)該事件。但該事件比較坑。例如過渡padding時,代碼如下:
#tempDiv {
padding: 1px;
transition-property: padding;
transition-duration: 1s;
}
#tempDiv:hover {
padding: 5px;
}
function showMessage() {
console.log('finished'); //過渡結(jié)束時觸發(fā)打印log
}
var element = document.getElementById("tempDiv");
element.addEventListener("transitionend", showMessage, false);
你可以代碼貼到瀏覽器里試試,結(jié)果出現(xiàn)4條log。因為過渡屬性指定的是padding,所以在padding-top,padding-right,padding-bottom,padding-left過渡結(jié)束時均觸發(fā)了transitionend事件。因此log被打印了4次。
如果上述CSS中將transition-property: padding;改為all,同樣會觸發(fā)4次。除非你改成transition-property: padding-top;這樣才能只觸發(fā)一次,但現(xiàn)實中只過渡一邊的場景非常少,通常都是4邊同時過渡,因此例如padding,margin,border之類的屬性,用transitionend事件會有多次捕捉的情況發(fā)生。
隱式過渡
transition過渡時有時會出現(xiàn)一些比較曖昧的情形,比如設(shè)成em的屬性,如你所知em是根據(jù)font-size來計算的。類似還有rem,vh,vw等都是根據(jù)另一個屬性的值來計算得到它的值。舉個例子padding:2em;,如果font-size被改變了,此時padding的“書面值”不變,仍舊是2em,但“實際值”將會發(fā)生變化并觸發(fā)transition過渡。這被稱作“隱式過渡”。多數(shù)瀏覽器會實現(xiàn)隱式過渡,但傳聞IE很特別,具體有多特別我也沒試過。沒試過就輕信傳聞好像很不嚴謹,但根據(jù)IE過往的口碑,我寧可信其有…前端工程師都懂的。
開關(guān)過渡和永久過渡
開關(guān)過渡,顧名思義就是觸發(fā)源的事件結(jié)束后會恢復到原始狀態(tài)。永久過渡就是過渡后不恢復到原始狀態(tài)。上面的例子都是開關(guān)過渡,當鼠標hover事件結(jié)束后,圖片恢復原始尺寸。但永久過渡的話,鼠標hover事件結(jié)束后,圖片仍舊保持放大后的尺寸。一圖勝千言,你可以點擊這里
//開關(guān)過渡
.transition {
transition: all 1s ease-in-out;
}
.transition:hover {
transform: scale(1.5);
}
//永久過渡
.forever {
transition: all 1s ease-in-out 999999s;
}
.forever:hover {
transform: scale(1.5);
transition: all 1s ease-in-out;
}
因為回到原始尺寸的transition-duration被設(shè)成了一個很大的時間,999999s差不多有12天,理論上你頁面開12天就能看到關(guān)閉過渡的效果,但現(xiàn)實等于永久過渡。該技巧無需任何JS腳本。
auto過渡
通常我們屬性過渡時,都是定值到定值的過渡,例如width:100px過渡到200px。但要過渡到width:auto就不行了。就算你指定transition: width 1s linear;會發(fā)現(xiàn)根本不會有1秒的平滑的過渡效果,而是瞬間完成過渡,你可以點擊這里
.div1 {
background-color: red;
width: 100px;
height: 50px;
}
#box1:hover {
width: auto;
transition: width 1s linear;
}
<div id="box1" class="div1"></div>
如果想要過渡效果,必須將auto轉(zhuǎn)換為固定值。那么問題來了,如何轉(zhuǎn)換呢?需要靠JS,通過getComputedStyle獲取auto后的固定值后,通過style設(shè)置該值,然后再觸發(fā)CSS的過渡效果。你可以點擊這里
window.onload = function(){
var box = document.getElementById("box2"),
originWidth = box.clientWidth,
width2AutoLater = null,
width2OriginLater = null;
var width2Auto = function(element, time) {
if (typeof window.getComputedStyle == "undefined") return;
var width = window.getComputedStyle(element).width;
element.style.width = "auto";
var targetWidth = window.getComputedStyle(element).width;
element.style.width = width;
setTimeout(function() {
element.style.transition = "width "+ time +"ms linear";
element.style.width = targetWidth;
}, 10);
};
var width2Origin = function(element, time) {
setTimeout(function() {
element.style.transition = "width 0s linear";
element.style.width = originWidth + "px";
}, 10);
};
function callLater(func, paramA, paramB){
return function(){
func.call(this, paramA, paramB);
};
}
width2AutoLater = callLater(width2Auto, box, 1000);
width2OriginLater = callLater(width2Origin, box, 1000);
box.addEventListener("mouseenter", width2AutoLater);
box.addEventListener("mouseleave", width2OriginLater);
}
<div id="box2" class="div1"></div>
代碼耐心看看應該能看明白。無CSS,全靠JS實現(xiàn)過渡效果。
思路:給div注冊了mouseenter和mouseleave事件來模擬hover效果。
mouseenter綁定width2Auto函數(shù),函數(shù)里臨時將div的width設(shè)為auto后,用getComputedStyle得到寬度值,將該寬度值和transition設(shè)進該div的style里。
mouseleave綁定width2Origin函數(shù),函數(shù)里將div的width改回初始值。
因為注冊事件的函數(shù)addEventListener的第二個參數(shù)是回調(diào)函數(shù)名,不能給回調(diào)函數(shù)傳參數(shù)。因此使用閉包的特性,定義了callLater中間函數(shù),函數(shù)里通過call調(diào)用上述兩個函數(shù)。
總結(jié)
我所知的transition差不多就這些,現(xiàn)在各瀏覽器對該屬性的支持力度應該是夠的,除非你還要照顧各種舊版本的瀏覽器,那可能細節(jié)方法還需要多加注意。可參考騰訊的這篇文章。