记一次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 = $("
").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 = $("
").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属性即可。

你可能感兴趣的:(javascript)