转载请注明英文原文及译文出处
原文地址:Issues with position fixed & scrolling on iOS
原文作者:Remy Sharp
译文地址:移动端 fixed 和 scroll 问题
译文作者:鎏金圣手火麒麟
最近在做iOS端的H5页面时,遇到了一个定位问题:
1、position: fixed 元素在页面滚动时属性值变为absolute,在页面停止滚动瞬间,才恢复fixed;
2、当使用 fixed 定位的元素,存在于进行滚动的容器元素内时,拖动容器元素会使出现闪动问题。
在寻求解决方案的过程中,看到了这一篇文章,对这个问题的原因解释得不错,故翻译过来,供大家参考。
翻译的过程中,对原文小标题做了修改,方便大家阅读。
翻译不对的地方,也可以在评论中帮忙指出,谢谢。
以下是译文:
随着iOS 5的发布,fixed 定位据说将会支持移动端Safari。
当然,这个说法并不一定是真的,因为在下文中我将给大家展示各种各样的bug。
顺带一提,在iOS 5的测试阶段,我已经向苹果的bug报告工具上传了一些bug,但天知道这个工具是怎么运行的,所以我不知道这些问题的编号(此处有争议,原文:the issue numbers)。
更新:基于 Corey Duston 指出关于 position fixed 的更多bug,我已经添加了 "scrolling == unusable position:fixed element"模块。
position:fixed, who cares?
我认为对于一个好的app而言,不一定非要使用到 fixed 定位。但是,我也注意到有越来越多的 iOS app 开始使用 fixed 定位的工具栏,比如 mini-MobileSafaris,苹果应用商店的原生 Facebook app 和 Instagram。
[图片上传失败...(image-c9ec28-1546570625041)]
[图片上传失败...(image-8a9dce-1546570625041)]
[图片上传失败...(image-16f460-1546570625041)]
AppStore via @devongovett, Facebook via @9eggs
问题
我已经创建了一些示例页面,方便大家自行查看。(以下视频来自油管,需要梯子)
问题1:抖动
如果你将 position: fixed
用任何正常的方式添加到桌面级页面中,你就会在滑动页面的时候,看到一定程度的抖动问题。
视频链接:https://www.youtube.com/embed/yps8Ea5GO4I?fs=1&feature=oembed
请注意这是在模拟器上出现了该bug,但我同样在 iPhone 真机上捕获了相同的bug。
模拟地址:http://jsbin.com/3/ixewok/6/
问题2:滑动时无法更新数据
眼尖的小伙伴可能已经发现了视频中的某些值发生了变化。我监控了 window.scrollTop
和 window.pageYOffset
(还有另一个值将会在下文提到)。你会注意到这两个值只有在页面滑动完全停止后才会发生变化。
当你想要在通讯录APP中模拟碰撞和分流式头部目录时,这就是个问题了。
(这句翻译的不好,原文:This is a problem if you want to monitor the page position to simulate effects like the bumping and shunting of category headings like you might see in the address book app.)
问题3:位置漂移
如果页面被放大到最大倍数,比如在 iOS 上,当用户将页面从纵向旋转到横向后,再进行任何比例超过1的缩放操作(比例放大),定位元素就会向上漂移。(我曾在其他网站见过元素彻底漂移出视窗的情况)
视频链接:https://www.youtube.com/embed/YIOdPf7jqK4?fs=1&feature=oembed
模拟地址:http://jsbin.com/3/ixewok/6/
问题4:获取焦点后跳跃
如果在 fixed 定位的元素中存在一个可以获取焦点的元素,比如 input 元素,这就有可能导致整个 fixed 元素跳到其他地方。这种情况只会在用户滑动页面的时候出现(如果这正是你想要的效果,那当我没说 XD)。
视频地址:https://www.youtube.com/embed/lrnvZDwgJRc?rel=0
模拟地址:http://jsbin.com/3/ixewok/8/
问题5:Scrolling == 无法使用的 position: fixed 元素
Corey Dutson指出还有另一个与 fixed 有关的bug。尽管在他的示例中滚动是通过JavaScript来实现,但核心问题是:如果页面是由脚本控制移动,那么在移动之后,fixed 定位的元素将不可使用。
从截屏中我已经做了记录,你可以通过iWebInspector看到,尽管 MobileSafari 已经在位置上渲染了 fixed 定位的元素,但它实际上并不在应该在的位置。当你再次触碰和移动页面时,真正的元素才会保持在正确的位置上。
视频地址:https://www.youtube.com/embed/R2MzdeJSCKw?fs=1&feature=oembed
模拟地址:http://jsbin.com/3/ixewok/13/
尽管我还没有找到这个bug的解决办法,并且我觉得可能这是 MobileSafari 自身的渲染问题,不过我还是会继续看看是否有办法解决。
解决抖动
随着 iOS 5 的发布,MobileSafari 同样支持了 -webkit-overflow-scrolling: touch
。这实际上是用于页面内容的行内块元素(这句不知如何翻译:I mean inline with respect to the document)。
如果我在前文的示例中改变 css,并且设置 html、body、内容块的高度为100%,然后将 scrolling touch 属性应用到内容上,那么抖动就会消失了。然而,这并不能彻底解决问题。
有个巧妙的办法是这样的:确保使用 fixed 定位的元素不是一个“移动画布”。这个示例展示了一个 fixed 元素放置在一个滚动元素的上方,但在dom结构中,它并不是滚动元素的子级。
所以当我尝试将这个办法用在 body
元素上时,抖动问题还是存在,因为 fixed 定位元素仍然位于滚动元素内。
视频地址:https://www.youtube.com/embed/suXz5dKtlcA?rel=0
我同样在真机上捕获了这个问题:点击这里。
模拟地址:http://jsbin.com/3/ixewok/10/
滚动位置更新
再来一次,仔细的小伙伴应该也注意到了某些值又发生了改变。请注意,由于我已经更改了CSS,因此 body 不再滚动,所以左右的 0 值分别对应 window.scrollTop
和 window.pageYOffset
。当窗口不可滑动时,内容块处于溢出状态,值也就不会改变。
但是,content.scrollX
的值仍在变化——但这不是默认的。
首先,你需要添加触摸事件,用来在用户滑动(或者触摸)时更新该值,所以在 JavaScript 中我可以添加:
content.ontouchstart = function() {};
touch 事件会发生在 start, end, move三个阶段,并且只需要一个值集(翻译的不好:a value set)。
(请注意,我并没有尝试直接将其设置为 true
——这可能也能起作用)。
然而,这依然是不完美的。你可以在上面的视频中看到,值只会在我触摸的时候更新。当我的手指离开屏幕,让页面惯性滚动时,值就不会更新了。
我仍会继续尝试有没有可能获取该值(无奈……)
总结 / TL;DR
1、不要在滚动元素内部使用 position: fixed
,否则它会出现抖动bug并且看起来很糟糕(我曾见过比视频中抖动得更疯狂的情况)。
2、确保使用 -webkit-overflow-scrolling: touch
3、如果你想要获取 scroll 的相关值,请确保你在滚动元素上添加了 touch 的监听事件。
同时,请在其他移动端浏览器也这么处理,不要只对苹果做这些处理。苹果对于 position: fixed
虎头蛇尾的做法真让人头疼,这作风跟巨硬真像。
Posted 24-May 2012
原文来自 https://segmentfault.com/a/1190000011525608