问题描述
2017年3月1号开始,iOS微信的浏览器内核从UIWebview切换到了WKWebview。在做兼容性测试的时候发现了WK内核对position:fixed
的解析跟以前不同。具体表现在:当页面下拉的触发页面的弹性滑动时,fixed的元素依然保持在原有的位置,而不随页面下拉。如下图:(因顾虑商业隐私,本文的所有页面内容将隐去)
如上图所示,页面下拉时,fixed在顶部的搜索框并不随之下拉,而页面其他部分触发弹性下拉,造成样式上与Android端不一致。(Android端和老内核下,fixed的导航栏会随着一起下拉)。尤其在搜索栏是半透明的时候,文字重叠在一起更为糟糕。(参考下图友商)
思路一
最外层使用一个宽高都是100%的容器,使用fixed定位。对这个容器设置overflow:scroll
,页面的所有内容都放这个容器里,并将需要固定在页面顶部的搜索栏使用position:absolute
固定住。
这样设置以后,页面内容是固定住了,遗憾的是页面的惯性滑动也失效了。带来的用户体验很糟糕。这方案显然不能用。
思路二
滑动到顶部的时候,使用JS阻止页面继续下拉。关键代码如下:
var lastY;
$(document.body).on('touchstart', function(e) {
lastY = e.changedTouches[0].clientY;
});
$(document.body).on('touchmove', function(e) {
var y = e.changedTouches[0].clientY;
var st = $(this).scrollTop();
if (y >= lastY && st <= 0) {
lastY = y;
e.preventDefault();
}
lastY = y;
});
这个方案也有不友好的地方:页面停留在顶部的时候,JS阻止了往下拉的动作,但是页面不是停留在顶部,而往下拉到超过顶部时,依然会触发弹性滚动。用户使用上的直观感受就是——页面卡死了,不能滑动了。
思路三
还是回到思路一,这次我们引入了-webkit-overflow-scrolling
。搜索框可以随页面一起下拉,如下图:
这个思路已经很接近我们的预期了,使用了-webkit-overflow-scrolling:touch
,使得页面内容跟以前一样能惯性滑动。把之前的fixed改成了absolute,导航栏可以一起下拉。(如果不想一起下拉就设置成fixed即可)。然而还是会有一些小问题:当页面停留在顶部时向下滑动,会导致最外层容器的滚动条不触发滑动,而整体页面会整体触发弹性下拉。所以这次触摸所触发不能拖动页面内容,在用户看起来就像是下拉出失败了。所以可以再用上思路二的代码,稍微改改如下:
var lastY;
$('.parent-element').on('touchstart', function(e) {
lastY = e.changedTouches[0].clientY;
});
$('.parent-element').on('touchmove', function(e) {
var y = e.changedTouches[0].clientY;
var st = $(this).position().top;
if (y >= lastY && st <= 0) {
lastY = y;
e.preventDefault();
}
lastY = y;});
现在用户停留在顶部的时候不能再下拉了,还是有些小遗憾的。
思路四
以上方案都不能满足我们的需求。最终我们采用了swiper插件控制整个页面的惯性滑动和回弹,Android端和IOS端终于保持了一致。相关代码就不贴出来了,swiper官网有参考案例。
后记
两个月后,系统更新已经修复了这个错误。现在您可以放心在页面中使用fixed布局,而不用如本文一样,大费周章去做hack。
Apple系统的浏览器还有其他诸多与规范不同的地方,给开发和设计带来许多麻烦。包括最近app store对React Native的封杀,都体现了Apple当前的一种态度。这像极了当年windows
XP风靡全球时的IE6,自恃极高的占有率,多少有点傲慢。
本文未经许可禁止转载,如需转载关注微信公众号【工程师加一】并留言。