问题描述
最近在做电商小程序,测试小姐姐上报了一个bug上来,如图:
中间的模态框期望是覆盖全屏的,但是实际上下方按钮区域没有覆盖到,而且这个问题只在iOS真机上存在。
我的布局大概是这样的
<view class="container" >
<scroll-view scroll-y="true" class="scroll">
<modal-component></modal-component>
</scroll-view>
<view class="bottom">
<button>立即购买</button>
<button>我来开团</button>
</view>
</view>
对应的样式:
.container{
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100vh;
}
.scroll{
flex:1;
}
.bottom{
display: flex;
height: 88px;
width: 100%;
}
其中modal-component是一个自定义组件,里边封装了一个fixed布局的模态框
大概是这样的:
<view>
<view class="modal">
<view class="modal-mask"></view>
<view class="modal-content"></view>
</view>
<button bindtap="bindtap">点击显示模态框</button>
</view>
对应的样式:
.modal {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 99999;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.modal-mask {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
background-color: rgba(0, 0, 0, .6);
}
.modal-content {
width: 200rpx;
height: 600rpx;
background-color: #fff;
}
这一切在开发者工具或者安卓手机上都表现很好,符合预期,fixed定位的模态框脱离了文档流,占满了整个viewport,但是在iOS却得到了最开始那张图里的效果。
嗯……万恶的iOS……
但是没办法,既然是前端,适配问题总是会遇到的,再不情愿也只能撸起袖子加油干。
问题排查
通过搜索微信小程序官方社区,我发现遇到这个问题的人不只我一个,比如:
19年的问题,到现在两年多了,期间微信官方只站出来说了一句:这是正常现象!
然后……然后就没然后了……
可以看到底下评论区大家也是对此愤怒又无奈:
既然官方推不动,看来还是只能我们自己来想办法,还好官方也不是什么都没有做,好歹给了一个关键词
-webkit-overflow-scrolling: touch
顺藤摸瓜,面向搜索引擎编程向来是我们的拿手好戏,顺利在这个css属性引发的其他问题(真是罪孽深重啊)的一篇文章中找到了线索:
也就是说,因为微信小程序官方在scroll-view上设置了-webkit-overflow-scrolling: touch,导致浏览器单独创建了一个UIScrollView, 内部的fixed定位元素,是基于这个UIScrollView来定位的,而不是根节点的Viewport。
既然导致问题的原因找到了,那么接下来就是选择解决方案了。
解决方案
要解决这个问题,有4个可能的思路:
- 调整整体布局,让scroll-view成为最外部容器,底部按钮也放到scroll-view内部,使用fixed定位到底部
- 把fixed元素放到scroll-view外面,通过父子组件通信,把显示模态框的逻辑放到父级
- 找到微信官方加给scroll-view的-webkit-overflow-scrolling: touch, 把值改成auto
- 弃用scroll-view,使用view配合overflow-y: auto来达成滚动效果
我们一个一个来分析。
- 调整布局并不复杂,底部按钮使用fixed定位以后,也只需要在列表底部通过padding等方式有一个占位就行,影响不大,可以考虑
- 整个页面中,需要内聚弹窗逻辑的组件不止一个,如果都放到父级,会导致父级逻辑过于臃肿,首先排除
- 改动最小,代价是需要牺牲一点滚动性能,也就是说把iOS用户的体验降到和Android用户一样的水平,可以接收,首选
- 页面中有滚动锚点相关的逻辑,普通的view没有bindscroll,无法实时获取滚动高度,如果用其他方式间接获取,开发成本和效果都不理想,也排除
经过分析,我首选3,也就是干掉-webkit-overflow-scrolling: touch,毕竟能少些点代码还是少写点代码white less do more是我的一贯宗旨,于是我们打开微信小程序的调试功能,看看scroll-view把这个该死的属性到底是加哪儿了。
然后找到这个webview,并且打开对应的调试控制台
document.getElementsByTagName('webview')[0].showDevTools(true)
顺利找到这个万恶之源(在开发者工具中这个属性显示无效,只有iOS真机上才行)
接下来的事情就简单了
.scroll .wx-scroll-view {
-webkit-overflow-scrolling: auto;
}
保存,运行,完美。
参考资料
scroll-view包含的自定义组件中fixed元素层级问题?
深入研究-webkit-overflow-scrolling:touch及ios滚动