在微信里面浏览页面的时候,有一个很管用的方法能够区分这个页面是原生的仍是H5形式的。随便打开一个页面,用力往下扯的时候,若是页面上方出现了“黑底”,黑底上有一行诸如网页由game.weixin.qq.com提供
的文字,就代表这个页面是H5形式的。这带来的问题是,若是一个页面可滚动区域很小,随便一拉,页面下方出现了黑底,而后你又轻轻往上一拉,上面的黑底又出来了,我的表示很是难受啊!
因而乎,折腾了一番,写了一个简单的组件来实现禁止这种拉动页面出现黑底的特性。php
首先须要说明的是,因为Android和IOS的webview存在差别,这个组件对于IOS是比较友好的,安卓下并不能作到完美避免,下面一一分析。node
智能手机和平板电脑一类的移动设备一般会有一个电容式触摸屏(capacitive touch-sensitive screen),以捕捉用户的手指所作的交互。有三种在规范中列出并得到跨移动设备普遍实现的基本触摸事件:ios
其中每个触摸事件都会包含三个触摸列表:git
这些列表由包含了触摸信息的对象组成:github
在这个组件中,咱们只须要用到e.touches[0].clientY
属性就够了:在开始触摸的时候,记录触摸点的起始位置,在手指移动过程当中,不断获取最新的clientY
,与起始位置的clientY
比较,就能获知拉动页面的方向。web
这三个属性是用来计算元素处于页面的哪一个位置的,考虑下面两种状况:chrome
经过上面两点,咱们已经知道要达到禁止出现黑底的效果,努力的方向是在知道滑动方向的条件下,在与height相关的属性达到临界值的时候及时阻止事件冒泡。只有三种简单的状况:移动web开发
总结起来以下表(1为容许,0为禁止,高位表示向上方向,低位表示向下方向)浏览器
能够拉的方向(height) | 拉的方向(touch) | 可否继续拉 |
---|---|---|
00 | 10 | 0 |
00 | 01 | 0 |
01 | 10 | 0 |
01 | 01 | 1 |
10 | 10 | 1 |
10 | 01 | 0 |
从表中咱们能够得出一个结论是,可否在该方向上继续拉其实就是对两种条件作一个&
运算!话很少说,上核心源码微信
// 防止过度拉动 preventMove: function(e) { // 高位表示向上滚动, 底位表示向下滚动: 1允许 0禁止 var status = '11', e = e || window.event, // 使用 || 运算取得event对象 ele = this, currentY = e.touches[0].clientY, startY = startMoveYmap[ele.id], scrollTop = ele.scrollTop, offsetHeight = ele.offsetHeight, scrollHeight = ele.scrollHeight; if (scrollTop === 0) { // 若是内容小于容器则同时禁止上下滚动 status = offsetHeight >= scrollHeight ? '00' : '01'; } else if (scrollTop + offsetHeight >= scrollHeight) { // 已经滚到底部了只能向上滚动 status = '10'; } if (status != '11') { // 判断当前的滚动方向 var direction = currentY - startY > 0 ? '10' : '01'; // console.log(direction); // 操做方向和当前容许状态求与运算,运算结果为0,就说明不容许该方向滚动,则禁止默认事件,阻止滚动 if (!(parseInt(status, 2) & parseInt(direction, 2))) { e.preventDefault(); e.stopPropagation(); return; } } },
开始的时候,我觉得上面的代码就万事大吉了,通过实践和摸索,结论是:简直是天真。
异步的概念之因此首先在Web2.0中火起来,是由于在浏览器中JavaScript在单线程上执行,并且它还与UI渲染共用一个UI线程。这意味着JavaScript在执行的时候UI渲染和响应是处于停滞状态的。 ----《深刻浅出nodejs》
这意味这什么呢?当咱们的UI线程在进行渲染的时候,JavaScript代码也是处于停滞状态的!不信的话能够在一个能够滑动的页面上引入下面这段代码:
var count = 0; setInterval(functiong() { console.log(++count); }, 100);
刷新页面的时候,控制台会一直打印不断变大的数字,可是只要你用手指开始拖动页面,打印终止,等你把手放开的时候,打印继续,并且数字会承接打印中止前那个数字。也就是UI在渲染的时候,js保存了状态,在UI渲染中止的时候,js又能够继续运行。
这对咱们的组件带来的影响是什么呢?几乎是毁灭性的,场景以下:
在寻求最终的解决方案以前,咱们先来讨论一下overflow这个属性。
传统 pc 端中,子容器高度超出父容器高度,一般使用 overflow:auto 可出现滚动条拖动显示溢出的内容,而移动web开发中,因为浏览器厂商的系统不一样、版本不一样,致使有部分机型不支持对弹性滚动,从而在开发中制造了所谓的 BUG。
从本人这两个月移动Web实践的经验来看,微信的webview里面overflow: scroll
和overflow: auto
的滑动效果不管是在安卓仍是IOS下的体验都很通常,有明显的卡顿现象,在安卓下面还会出现滑动过快的时候在页面停下来以后滚动条才闪到相应位置的现象。
在IOS5以后,出现了一个新的属性: -webkit-overflow-scrolling
,用来控制元素在移动设备上是否使用滚动回弹效果。它的取值有两个:
实验代表,在IOS下,对一个元素设置了overflow:scroll
的基础上再添加-webkit-overflow-scrolling: touch;
会让滑动又如丝般顺滑。
这个属性和咱们解决以前的问题有什么联系呢?秘密就在这弹性滚动效果。
页面中body
元素的内容超过一屏,页面能够往下滑动(手指往上拉)。按照咱们组件的设定,手指开始的时候是不能往下拉的,可是若是手指的方向是先往上拉一小段,在手指不离开屏幕的基础上再往下拉,当页面拉到顶部的时候,会相继出现黑底,由于UI在渲染,js无法去阻止事件冒泡。
如今咱们把组件的做用元素设定为body内最外围的div元素,而且给这个元素添加两个CSS属性overflow:scroll
和-webkit-overflow-scrolling: touch;
,那么上面的场景就会变成:
页面中body内最外围的div标签内容超过一屏,其内容能够往下滑动(手指往上拉)。按照咱们组件的设定,手指开始的时候是不能往下拉的。和以前同样,手指先往上拉一小段,在手指不离开该元素的基础上再往下拉,当元素内容到顶以后,由于UI在渲染,js本插不上手,可是该元素内部的内容设置了弹性滚动,要实现弹性滚动,基本要求就是这个div容器是不动的,能够理解成由于弹性滚动,自动就禁止掉了事件冒泡,也就不会出现黑底了。
确定有人要问了,既然自动禁止了事件冒泡,那还要这个组件何用?固然有用,会禁止掉事件冒泡的前提是内容在滚动。依照上面的场景,若是一开始手指直接往下拉,没有组件的限制,仍是会露出黑底,于是,要实现比较好的效果,是须要这两个属性和组件配合的。
至于安卓嘛,由于没有这个属性,暂时只能一边凉快去吧。
多说无用,看源码吧:
https://github.com/yuanzm/preventoverscrolljs