在写 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属性即可。