在寫 jQuery WeUI 的一個picker的彈出動畫的時候,我是通過CSS動畫實現的。
CSS代碼如下:
.weui-picker-modal {
width: 100%;
position: absolute;
z-index: 100;
bottom: 0;
text-align: center;
border-radius: 0;
opacity: 0.6;
color: @color-text;
transition-duration: .3s;
height: 13rem;
background: #EFEFF4;
transform: translate3d(0, 100%, 0);
transition-property: transform, opacity;
&.weui-picker-modal-visible {
opacity: 1;
transform: translate3d(0,0,0);
}
}
JS代碼如下:
$.openPicker = function(tpl) {
var container = $("<div class='weui-picker-container'></div>").appendTo(document.body);
container.show();
container.addClass("weui-picker-container-visible");
//關於佈局的問題,如果直接放在body上,則做動畫的時候會撐開body高度而導致滾動條變化。
var dialog = $(tpl).appendTo(container); //
dialog.show(); //注意這一行奇怪的代碼
dialog.addClass("weui-picker-modal-visible");
return dialog;
}
其實原理很簡單,就是創建DOM的時候,通過 translate 把彈窗Y軸向下平移 100%,於是就看不到了,然後顯示的時候再設置 Y 軸平移爲0,就可以看到一個CSS動畫的彈出效果。
但是測試發現在 iOS版的微信中就無法出現彈出動畫,安卓以及PC上都沒問題。
按道理講是應該有動畫的,因爲我是先創建了DOM,並插入到頁面,然後才添加了 weui-picker-modal-visible
類做動畫。但是事實卻是iOS上第一次沒有彈出動畫,那麼也就意味着其實在IOS上,創建DOM和添加 weui-picker-modal-visible
其實是合併成了一步,纔會出現打開的時候沒有彈出動畫。
注意這一行奇怪的代碼 dialog.show()
,其實這一行代碼就是想確保在添加類之前讓DOM已經創建完畢。然後突然想到之前我有研究過JS性能優化相關的內容,其中有一條大意就是“如果連續對DOM進行多次操作,瀏覽器可能會調整這些操作的順序以提升性能”,其實就是說瀏覽器會把DOM的修改操作儘量提前到被插入文檔流之前進行,因爲插入文檔流之前進行修改就不需要進行渲染,所以這裏其實如下三行代碼在IOS中其實被提前到了DOM插入操作前:
var dialog = $(tpl).appendTo(container); //
dialog.show(); //注意這一行奇怪的代碼
dialog.addClass("weui-picker-modal-visible");
本來是先把DOM插入文檔流,然後進行DOM操作,優化後大致相當於變成了這樣:
var dialog = $(tpl);
dialog.show(); //注意這一行奇怪的代碼
dialog.addClass("weui-picker-modal-visible");
dialog.appendTo(container); //把上述三行代碼先執行,然後再插入文檔流,以提升性能。
那麼很明顯,這樣做是無法出現動畫的。而如何避免出現這種情況呢,之前的博客也提到了,就是在插入文檔流之後,執行一次讀取CSS屬性操作,於是瀏覽器不得不立刻執行插入操作才能計算出CSS的屬性值:
$.openPicker = function(tpl) {
var container = $("<div class='weui-picker-container'></div>").appendTo(document.body);
container.show();
container.addClass("weui-picker-container-visible");
//關於佈局的問題,如果直接放在body上,則做動畫的時候會撐開body高度而導致滾動條變化。
var dialog = $(tpl).appendTo(container);
dialog.width(); //改動這一行代碼,通過取一次CSS值,強制瀏覽器不能把上下兩行代碼合併執行,因爲合併之後會導致無法出現動畫。
dialog.addClass("weui-picker-modal-visible");
return dialog;
}
只需要改動一行代碼即可解決這個bug。之前認爲進行一次 show
操作就可以保證已經實際插入文檔流了,這種想法是錯的,只有進行一次CSS屬性的讀取才能百分百保證。
可能有人要問爲什麼只有iOS上有這個問題,那估計是因爲iOS更加重視這種UI上的性能優化,所以儘量進行DOM操作的合併。
所以如果大家以後自己寫CSS動畫,創建一個DOM之後立刻加一個類進行動畫,也有可能會碰到這種bug,那麼只需要在添加動畫類之前隨意讀取一個CSS屬性即可。