解決transition動畫與display沖突的幾種方法


demo(如果沒有顯示,請查看源地址http://jsfiddle.net/ihardcoder/HNduT/2/)所示,基本的效果是在點擊“Translate”按鈕后,藍色區域透明度變為0,然后隱藏display:none;點擊Reset按鈕后,首先顯示藍色區域display:block,然后透明度逐漸恢復至1,代碼如下:

 1 var btn1 = $("#testbtn1");
 2 var btn2 = $("#testbtn2");
 3 var container = $("#container");
 4 
 5 btn1.on('click', function(e) {
 6     container.css({
 7         "transition": "opacity 1s",
 8         "-webkit-transition": "opacity 1s",
 9         "-moz-transition": "opacity 1s",
10         "-o-transition": "opacity 1s",
11         "-ms-transition": "opacity 1s",
12         "opacity": "0.1"
13          });
14     setTimeout(function() {
15         container.css("display", "none");
16     }, 1000);
17 });
18 btn2.on('click', function(e) {
19     container.css("display","block");
20     container.css("display");
21     container.css("opacity","1");
22 });

上述代碼中第20行看起來很奇怪,可能會有人疑問這句代碼的作用,事實是,如果沒有這句代碼,在點擊Reset后得到的效果是:藍色區域瞬間顯示出來,並沒有透明度改變的過渡效

至於產生這種現象的原因,深層次的機制我也尚未搞明白,暫時理解為CSS3的transition過渡不支持display的改變,直接操作display會破壞transition的動畫,所以在第14行通過setTimeout將opacity的transition動畫與display的操作分隔。

而第20行代碼的目的,我是這樣理解的,瀏覽器的UI線程在處理UI操作時,將多個css屬性的set操作加入在同一個tick中處理(關於瀏覽器處理tick機制,請參考http://www.infoq.com/cn/articles/javascript-high-performance-animation-and-page-rendering?utm_source=infoq&utm_medium=popular_links_homepage),也就是說,如果不插入第20行代碼,第19行和第21行的css屬性set操作將會被同時執行,所以將會得到瞬間顯示出來的效果;第20行代碼其實是css屬性的get操作,我的理解是,如果在兩個css屬性的set操作中間插入get操作,UI線程在處理的時候將會按順序執行,display的操作和opacity的操作在不同的tick中被執行,這樣便的到我們想要的過渡效果。

第二種方法,由於display對transition的破壞作用,還有另外一種方法來hack,沒有錯,就是setTimeout!(這貨完全是js的大殺器!)代碼如下:

1  btn2.on('click', function(e) {
2     container.css("display","block");
3     setTimeout(function(){
4        container.css("opacity","1");
5     },delay);
6  });

但是用setTimeout的方法有一個弊端,第5行的delay在不同的瀏覽器(甚至不同版本的相同瀏覽器)中需要設置不同的數值,經本人測試,chrome35和IE10下delay=0即可,Firefox30下delay>=14.

第三種解決方法是通過window.requestAnimationFram來實現,代碼如下:

1 btn2.on('click', function(e) {
2       container.css("display","block");
3       requestanimationframe(function(){
4          container.css("opacity","1");
5       });
6    });

requestAnimationFram的作用是將opacity的操作推遲到下一個tick中處理,從而將display的操作分隔開,基本原理與setTimeout相同。

另外,關於display為何會破壞transition動畫,目前本人仍未找到相關資料來證明其內部機制,我的個人理解是,display的操作會觸發瀏覽器的reflow操作,而transition支持的效果只是觸發瀏覽器的repaint操作,回到上面的demo,如果我們通過visibility屬性來控制顯示與隱藏,則不會破壞transition的效果。所以,可以暫時這么認為:reflow與repaint的混合會破壞transition的動畫效果,至於更深層次的原因嘛,借我借我一雙慧眼吧~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM