媒介
在微信内里阅读页面的时刻,有一个很管用的要领能够辨别这个页面是原生的照样H5情势的。随意翻开一个页面,用力往下扯的时刻,假如页面上方涌现了“黑底”,黑底上有一行诸如网页由game.weixin.qq.com供应
的笔墨,就表明这个页面是H5情势的。这带来的题目是,假如一个页面可转动地区很小,随意一拉,页面下方涌现了黑底,然后你又悄悄往上一拉,上面的黑底又出来了,个人示意异常难熬痛苦啊!
于是乎,折腾了一番,写了一个简朴的组件来完成制止这类拉动页面涌现黑底的特征。
完成道理
起首须要申明的是,由于Android和IOS的webview存在差别,这个组件关于IOS是比较友爱的,安卓下并不能做到圆满防备,下面逐一剖析。
简述touch事宜
智能手机和平板电脑一类的挪动装备平常会有一个电容式触摸屏(capacitive touch-sensitive screen),以捕获用户的手指所做的交互。有三种在范例中列出并获得跨挪动装备普遍完成的基础触摸事宜:
touchstart :手指放在一个DOM元素上
touchmove :手指拖曳一个DOM元素
touchend :手指从一个DOM元素上移开
个中每个触摸事宜都邑包含三个触摸列表:
touches :当前位于屏幕上的一切手指的一个列表。
targetTouches :位于当前DOM元素上的手指的一个列表。
changedTouches :触及当前事宜的手指的一个列表。
这些列表由包含了触摸信息的对象构成:
identifier :一个数值,唯一标识触摸会话(touch session)中的当前手指。
target :DOM元素,是行动所针对的目的。
客户/页面/屏幕坐标 :行动在屏幕上发作的位置。
半径坐标和 rotationAngle :画出约莫相当于手指外形的椭圆形。
在jsfiddle内里写一个简朴的小demo就一览无余了:
http://jsfiddle.net/yuanzm/ws9j4v1v/2/
在这个组件中,我们只须要用到e.touches[0].clientY
属性就够了:在最先触摸的时刻,纪录触摸点的肇端位置,在手指挪动过程当中,不停猎取最新的clientY
,与肇端位置的clientY
比较,就可以获知拉动页面的方向。
与height相干的几个属性
scrollHeight: 是计量元素内容高度的只读属性,包含overflow款式属性致使的视图中不可见内容。没有垂直转动条的状况下,scrollHeight值与元素视图添补一切内容所须要的最小值clientHeight雷同。包含元素的padding,但不包含元素的margin.
offsetHeight:是一个只读属性,它返回该元素的像素高度,高度包含该元素的垂直内边距和边框,且是一个整数。
scrollTop:设置猎取读取元素向上转动了若干像素。关于可转动的元素,这个值是可见地区顶部和不可见地区顶部的间隔。假如元素不能转动,这个值默认为0。
这三个属性是用来盘算元素处于页面的哪一个位置的,斟酌下面两种状况:
元素的offsetHeight大于即是scrollHeight,无纵向转动条涌现,这个元素是不能转动的。假如一个元素不能转动了,就会尝试外层的元素能不能转动,一层一层往外冒泡。在webview内里,最表面一层就是这个webview容器了,根据微信的设置,这一层的“转动”就是显露下面的黑底。所认为了防备显露黑底,我们要在当前元素不能转动的时刻实时制止掉冒泡,如许就不会触发到上一层的转动。
假如一个元素设置了高度,而且设置了overflow: scroll,当元素内的内容可转动的时刻,scrollHeight的值就会显著大于offsetheight,那我们怎样推断元素内的内容下拉到底部了呢?这就须要综合offsetHeight和scrollTop的值了,假如offsetHeight的值加上srcollTop的值大于即是scrollHeight的值,就表明内容已滑动底部了。和第一点一样,当我们晓得了临界条件后,实时阻挠掉冒泡就ok了。
连系touch和height属性
经由过程上面两点,我们已晓得要到达制止涌现黑底的效果,勤奋的方向是在晓得滑动方向的条件下,在与height相干的属性到达临界值的时刻实时阻挠事宜冒泡。只需三种简朴的状况:
(内容)向下拉到底部,不能往下拉,然则能够往上拉
(内容)向上拉到顶部,不能往上拉,然则能够往下拉
(内容)既不能往下拉也不能往下拉
总结起来以下表(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;
}
}
},
与UI共用的线程
最先的时刻,我认为上面的代码就高枕无忧了,经由实践和探索,结论是:简直是无邪。
异步的观点之所以起首在Web2.0中火起来,是由于在阅读器中JavaScript在单线程上实行,而且它还与UI衬着共用一个UI线程。这意味着JavaScript在实行的时刻UI衬着和响应是处于阻滞状况的。 —-《深入浅出nodejs》
这意味这什么呢?当我们的UI线程在举行衬着的时刻,JavaScript代码也是处于阻滞状况的!不信的话能够在一个能够滑动的页面上引入下面这段代码:
var count = 0;
setInterval(functiong() {
console.log(++count);
}, 100);
革新页面的时刻,掌握台会一向打印不停变大的数字,然则只需你用手指最先拖动页面,打印住手,等你把手摊开的时刻,打印继承,而且数字会承接打印住手前谁人数字。也就是UI在衬着的时刻,js保留了状况,在UI衬着住手的时刻,js又能够继承运转。
这对我们的组件带来的影响是什么呢?几乎是毁灭性的,场景以下:
假如页面内容不足一屏,根据组件的设定,既不能上拉也不能下拉,这类状况不会受影响。
假如页面内容多于一屏,根据组件的设定,这时刻能够往下拉不能往上拉,在尝试上拉的时刻,组件会阻挠冒泡。但假如先下拉一点然后用力往上拉,原本拉到顶以后组件会阻挠事宜冒泡,然则一旦下拉以后,线程就归属于UI了,上拉的过程当中组件的推断完整插不进手,照样无情漏出了黑底!GG!
可爱的IOS5新特征
在追求终究的处理方案之前,我们先来讨论一下overflow这个属性。
传统 pc 端中,子容器高度超越父容器高度,平常运用 overflow:auto 可涌现转动条拖动显现溢出的内容,而挪动web开辟中,由于阅读器厂商的体系差别、版本差别,致使有部份机型不支持对弹性转动,从而在开辟中制作了所谓的 BUG。
从本人这两个月挪动Web实践的履历来看,微信的webview内里overflow: scroll
和overflow: auto
的滑动效果无论是在安卓照样IOS下的体验都很平常,有显著的卡顿征象,在安卓下面还会涌现滑动过快的时刻在页面停下来以后转动条才闪到响应位置的征象。
在IOS5以后,涌现了一个新的属性: -webkit-overflow-scrolling
,用来掌握元素在挪动装备上是不是运用转动回弹效果。它的取值有两个:
auto:运用一般转动, 当手指从触摸屏上移开,转动会马上住手。
touch:运用具有回弹效果的转动, 当手指从触摸屏上移开,内容会继承坚持一段时候的转动效果。继承转动的速率和延续的时候和转动手势的猛烈水平成正比。同时也会建立一个新的客栈高低文。
试验表明,在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