記一次CSS彈出動畫的Bug

在寫 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屬性即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章