出人意料的滑动
滑动,对于移动端来说多么简单的一个动作,上下动动手指头呗,就好比pc上的滚滚鼠标,你说动个手指头能弄出个啥大事儿啊...emmmm , 图样图森破, 没做过之前我也是这样想的 , 但是亲身碰到了那才叫一个酸爽٩(๑❛ᴗ❛๑)۶.
这次的要求做一个局部区域滚动,页面结构大致分为上中下三个结构 , 上面是一个固定的黑色播放窗口,中间就是滚动区域,下面是一个操作导航;
当时在chrome的手机模拟器上看的效果,没有发生任何问题,但是后面用真实手机一测...瞬间爆炸
局部滚动-webkit-overflow-scrolling:touch引发的"惨案"
滑动不流畅
顶部一个固定的播放展示页,中奖评论交流,底部一个发送消息输入框,如图所示;
按照基本布局之后,在中间部分(mian)使用
.main{
overflow-y:scroll;
-webkit-overflow-scrolling:touch;
}
得到滚动效果,注意这2个属性,首先overflow-y:scroll;
不用多说,这个都知道,是内容超出容器高度之后,容器出现滚动条用的,这时候安卓和ios的差异性就来了,在安卓上你可以发现滚动没有任何异常,但是在ios上就会滑动异常卡顿,完全划不动的感觉;
所以加上了第二句-webkit-overflow-scrolling:touch;
这东西的功能很强大,可以解决大部分ios局部滚动不流畅的问题;
那么这个-webkit-overflow-scrolling:touch;到底是什么?
一个只有 iOS 设备支持的非标准属性。苹果自己的解释:指定是否在 overflow: scroll 的元素中使用“原生”的滚动方式
他包含两个可选值:auto 和 touch
auto:就是普通的无惯性滚动效果(也就是上面说到的异常卡顿,完全划不动的情况,这是默认值)
touch:原生的滚动效果。(也就是说可以获得跟原生app一样流畅的滚动效果);使用此效果会构造一个 stacking context !什么是 stacking context?这可以说是CSS里一个阴暗面,极其晦涩。我是没看明白ヾ(=・ω・=)o) 总之所有的坑都是由此而起!!
滑动卡住
这个问题的原因其实也是-webkit-overflow-scrolling造成的;
**1, 在safari上,使用了-webkit-overflow-scrolling:touch之后,页面偶尔会卡住不动。
2, 在safari上,点击其他区域,再在滚动区域滑动,滚动条无法滚动的bug。
3, 通过动态添加内容撑开容器,结果根本不能滑动的bug。**
其中第一个问题主要是当滚动区域内容滚到顶或者滚到底就会出现此问题,当到顶和到底之后再滚动,这时候如果接着滚动的话,他就可能会连同整个页面一起卷动,这时候在滑动就会卡住(与其说是卡住更不如说是被body粘住...很难以形容 (〃´皿`)q);
第二个问题就是在滚动过程中,如果点击其他地方,在回来滚动也会划不动
第三个问题我暂时没遇到
另外这个滚动我甚至感觉还和手指滚动力度,动作幅度有关系哦...总之很奇怪!更加奇怪的是在百度上甚至很难找到对应的关键词条
补充一些其他资料
简单翻译就是:如果我在一个滚动div里面使用了-webkit-overflow-scrolling:touch,它可以获得堪比原生滚动一般的效果,但是,div它自己有时候会冻结(?)并且不会响应我的手指滑动,在2-3秒之后才可以再一次滚动(英语2级都没过的渣渣翻译ヾ(◍°∇°◍)ノ゙,但是这个情况确实跟我上面的问题极为相似);
而这个偶尔卡住的问题,解决方案网上众说纷纭,遇到了很多相同的说法,比如如果卡住不动的话,就加一个z-index,就能解决该问题的说法。
在试了很多次之后,这种说法没有一次解决过这个问题。这个说法能够传播出来,可能是使用者当时在使用的时候遇到了-webkit-overflow-scrolling:touch点透或者层级的问题。所以该方案不具有适用性。
所以这个东西真的让我很苦恼了很久,以致于那段时间所有的滚动条不是通过body自己滚动,就是使用iScroll这样的库;
如果出现偶尔卡住不动的情况,那么在使用该属性的元素上不设置定位或者手动设置定位为static;
这样会解决部分因为定位(relative、fixed、absolute)导致的页面偶尔不能滚动的bug。
但是滑动到顶部继续手指往下滑,或者到底部继续往上滑,还是会触发卡住的问题(其实是整个页面上下回弹),说他算bug,其实就是ios8以上的特性,如果滚动区域大一点,用户不会觉得这是bug,如果小了,用户会不知道发生了什么而卡住了。
附上原文地址:深入研究-webkit-overflow-scrolling:touch及ios滚动
滚动中 scrollTop 属性不会变化。
之前这个的确是发生了,在滚动过程当中不会发生取值变化,只有在滚动结束时候发生一次,但是现在我竟然无法重现这个bug了,此bug目前暂无解决办法...(TAT)...后续补上
ps: 这个问题产生的原因还是内核的问题,新版的微信浏览器已经修复了这个bug,其他的第三方浏览器也不会有这个问题,他们都已经转向了更为优秀的WKWebView,但是你在手机QQ上自带的那个浏览器上就会遇到这个问题,取不到scrollTop 的值;至少到我写这个附注的这一刻为止,这个问题还没有得到解决;
手势可穿过其他元素触发元素滚动
这个东西也有人管他叫做滚动穿透,在h5上还有一个点击穿透(点透)问题,这两个问题总结;滚动穿透基本表现为当在上层(z-index较大的)元素上滚动时,底层元素会跟着滚动,最常见在遮罩弹窗上滚动,body也会跟着滚;你可以在显示半透明蒙版时将 ul 的 -webkit-overflow-scrolling: touch 或 overflow: scroll 去掉,但是会造成屏幕明显的闪烁。如果给 body 的 touchmove事件 preventDefault() 可以防止触发滚动,但是是所有滚动区域都会失效.
关于内核一些历史记录文章
说到底那么造成以上种种问题的原因到底是什么呢?有没有什么好的解决方案呢?
由于苹果公司对安全性等原因的考虑,苹果公司静止第三方浏览器在 iOS 设备上使用自己的浏览器的内核,换句话说,使用自己内核的浏览器都被禁止上架 AppStore。各大厂商无奈,于是长久以来,包括 Chrome 在内的所有第三方浏览器,都只是使用 iOS 系统内置的浏览器控件包一层外壳,(这在国内被称为壳浏览器,其实国内大部分浏览器都是这样的,号称双核甚至多核浏览器的那些浏览器,他们的兼容模式其实就是使用的ie内核(trident),急速模式就是webkit内核,关于内核之战后面开一个闲谈)这个控件就是 UIWebView。这个 UIWebView 不仅速度差,HTML5 支持率低,占用内存高,还有各种各样奇怪的问题。然而苹果公司却给自己的 Safari 浏览器开了后门。首先 Safari 使用的支持 JIT 编译的 JS 引擎内核 Nitro 比 UIWebView 里老旧的解释性 JavaScriptCore 内核速度搞数倍,然后 HTML5 支持度也比 UIWebView 高,还少了某些奇葩bug。久而久之就形成了 iOS 设备上 Safari 浏览器全面碾压其他第三方浏览器的现象。
在乔帮主撒手人寰不久之后,苹果公司口气终于松动,虽然没有放开第三方浏览器内核的限制,但把 Safari 的浏览器内核提取了出来开放第三方浏览器使用,那就是如今的 WKWebView(WK 即 Webkit 的缩写)。但由于 WKWebView 只支持 iOS8 以上系统,各大浏览器厂商并未立刻跟进。直到最近的 iOS9 时代,Chrome 成为第一个吃螃蟹的 APP,使用了 WKWebView 内核。测试数据表明,使用 WKWebView 内核的 Chrome 浏览器在速度和 HTML5 支持率上已经与 Safari 浏览器不相上下。紧接着 Mozilla 公司宣布 Firefox 登录 iOS 平台,使用的也是 WKWebView 内核(于是有了第一款基于 Webkit 内核的火狐浏览器 :)
不知苹果做了什么手脚,也许苹果的开发人员认为 WKWebView 的效能已经足以支撑在 scroll 事件中执行额外代码而不造成 UI 卡顿,总之在 WKWebView 内核中滚动可以正常触发 scroll 事件,当然也能正常获得 scrollTop 的值。但是滚动穿透的问题依然存在。
最后
在经历了那么多问题之后,终于找到了一款优秀的插件better-scroll,最后这个问题终于得到解决;详情可以百度better-scroll
better-scroll的常用参数和方法
//一个简单的例子
<div class="wrapper"> //请注意检查这东西和.content的高度,假设划不动你可以看看他们的css是不是内外城高度错了
<div class="content">
content...
</div>
</div>
<script>
var wrapper=$('#wrapper);
var myScroll=new BScroll(wrapper,{ //直接实例化就好,参数可根据实际情况或查看使用说明添加
HWCompositing:true,
useTransition:false, // 防止iphone微信滑动卡顿
probeType: 3,
click:true,
wheel:false,
snap:false,
});
</script>
**
在使用bs的时候值得注意的几点
1,是假设在滚动区域里面夹杂了图片,那么可能会因为图片的加载问题导致betterscroll高度计算不正确,所以可以的话给图片绑定上一个onload事件,然后调用他的refresh();
<img onload="imgRefresh()">
imgRefresh(){
myScroll.refresh();
}
2,任何动态添加元素进去滚动区域的时候(比方append的时候),最好都调用一次refresh()方法,让他自己重新计算一次滚动距离,防止意外
3,scroll实例化时候的选择器问题,一定要保证唯一性 也就说加入你是用的是jquery的class选择器,比如class为wrap 那么一定要使用$('.wrap')[0]来确保唯一性;或者你可以使用原生选择器方法 document.querySelector(selectors[, NSResolver]);
4,scroll滑动区域如果碰上了display:none; 那么极有可能会出现实例化了但是无法滚动的情况,因为display:none 的元素在页面上是没有宽高属性,只会得到0或者其他的什么稀奇古怪的值,所以插件只会拿到完全错误的宽高数据,那么就无法正确实例化,
要解决这个问题的话就是用他提供的refresh()方法,你只需要在点击显示遮罩的时候手动refresh()就好了
var bsw=cbs(); //创建bs 实例
$('.showbs').click(function(event) { //点击显示bs 弹窗
$('.option-mask').show();
bsw.refresh(); //手动刷新,强制计算bs实例
});
隐藏的时候什么都不用管,普通hide()就好
5, 请注意内容的包裹元素和外层父元素的高度关系
实例化的那个dom元素的高度一定是固定的,而且必须小于内容的直系包裹元素,只有这样才能产生滚动落差,有时候你很容易实例化错误的dom对象;然后内容的直系包裹元素你不能有高度属性,他的高度就是里面内容高度;
2022.04.13更新: 今天闲来无事又去试了一下,在ios 13.6版本下里面的Safari 貌似已经没有这个问题了
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。