原文地址: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"模块。
我认为对于一个好的app而言,不一定非要使用到 fixed 定位。但是,我也注意到有越来越多的 iOS app 开始使用 fixed 定位的工具栏,比如 mini-MobileSafaris,苹果应用商店的原生 Facebook app 和 Instagram。
AppStore via @devongovett, Facebook via @9eggs
我已经创建了一些示例页面,方便大家自行查看。(以下视频来自油管,需要梯子)
如果你将 position: fixed
用任何正常的方式添加到桌面级页面中,你就会在滑动页面的时候,看到一定程度的抖动问题。
视频链接:https://www.youtube.com/embed/yps8Ea5GO4I?fs=1&feature=oembed
请注意这是在模拟器上出现了该bug,但我同样在 iPhone 真机上捕获了相同的bug。
模拟地址:http://jsbin.com/3/ixewok/6/
眼尖的小伙伴可能已经发现了视频中的某些值发生了变化。我监控了 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.)
如果页面被放大到最大倍数,比如在 iOS 上,当用户将页面从纵向旋转到横向后,再进行任何比例超过1的缩放操作(比例放大),定位元素就会向上漂移。(我曾在其他网站见过元素彻底漂移出视窗的情况)
视频链接:https://www.youtube.com/embed/YIOdPf7jqK4?fs=1&feature=oembed
模拟地址:http://jsbin.com/3/ixewok/6/
如果在 fixed 定位的元素中存在一个可以获取焦点的元素,比如 input 元素,这就有可能导致整个 fixed 元素跳到其他地方。这种情况只会在用户滑动页面的时候出现(如果这正是你想要的效果,那当我没说 XD)。
视频地址:https://www.youtube.com/embed/lrnvZDwgJRc?rel=0
模拟地址:http://jsbin.com/3/ixewok/8/
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
——这可能也能起作用)。
然而,这依然是不完美的。你可以在上面的视频中看到,值只会在我触摸的时候更新。当我的手指离开屏幕,让页面惯性滚动时,值就不会更新了。
我仍会继续尝试有没有可能获取该值(无奈……)
1、不要在滚动元素内部使用 position: fixed
,否则它会出现抖动bug并且看起来很糟糕(我曾见过比视频中抖动得更疯狂的情况)。
2、确保使用 -webkit-overflow-scrolling: touch
3、如果你想要获取 scroll 的相关值,请确保你在滚动元素上添加了 touch 的监听事件。
同时,请在其他移动端浏览器也这么处理,不要只对苹果做这些处理。苹果对于 position: fixed
虎头蛇尾的做法真让人头疼,这作风跟巨硬真像。
Posted 24-May 2012
手机扫一扫
移动阅读更方便
你可能感兴趣的文章