umo-tap-scroll v0.0.2
tap-scroll
用于在 FastClick 环境下,各 WebView 进行 scroll 状态时手指按下阻止滚动时,阻止当次 click 事件的触发
Usage
npm install umo-tap-scroll
代码层只需新增 2 行
// 引用一下
import TapScroll from "umo-tap-scroll";
// 具体是直接 window.FastClick 还是 import 还是 require 随意,保证可以拿到FastClick符号即可
import FastClick from "fastclick";
// 加入一行解决
TapScroll.attachFastClick(FastClick);
// 业务本身就需要初始化
FastClick.attach(document.body);
lib/fix
fixTimeStamp
在 iOS 11.3+
和 Chrome 49+
,Event.prototype.timeStamp
从时间戳(Date.now
)变成了时间差(performance.now
),提供了如下方法。
在
event
传入handler
前,将timeStamp
赋值为时间戳,在handler
处理完成后,还原成原来的样子
import { fixTimeStamp } from "umo-tap-scroll/lib/fix";
// 必须在首次 attach 之前执行fix逻辑
fixTimeStamp(FastClick);
fixPassive
在 iOS 11.3+
,开始支持 PassiveEventListener
在
FastClick
的attach
和destroy
阶段,使用{ passive: false }
来处理事件
import { fixPassive } from "umo-tap-scroll/lib/fix";
// 必须在首次 attach 之前执行fix逻辑
fixPassive(FastClick);
fixFocus
在 iOS 11.3+
,FastClick 需要主动调用 target.focus
才能触发键盘的弹起
import { fixFocus } from "umo-tap-scroll/lib/fix";
// 必须在首次 attach 之前执行fix逻辑
fixFocus(FastClick);
Swiper 兼容
在FastClick
配合使用Swiper
时,会有手指侧滑导致触发click
的情况,可以按照如下方式做处理:
import TapScroll from "umo-tap-scroll";
new Swiper({
...swiperOptions,
onInit() {
TapScroll.swiperOnInit();
},
onTap(swiper, event) {
// 可能会 preventDefault
TapScroll.swiperOnTap(swiper, event);
},
onTouchEnd(swiper, event) {
// 可能会 preventDefault, stopPropagation
TapScroll.swiperOnTouchEnd(swiper, event);
},
});
测试验证
分别使用 UIWebView, WKWebView, U3, U4 打开http://site.alipay.net/h5-lib/tap-scroll/fastclick/,体验实际效果
- meta + FastClick + target.scroll
使用 FastClick,并且 scroll 是通过
overflow:scroll
来实现的
- meta + FastClick + window.scroll
使用 FastClick,并且 scroll 是在 window 上的
- meta + TapScroll + target.scroll
使用 TapScroll,并且 scroll 是通过
overflow:scroll
来实现的
- meta + TapScroll + window.scroll
使用 TapScroll,并且 scroll 是在 window 上的
- meta + target.scroll
scroll 是通过
overflow:scroll
来实现的
- meta + window.scroll
scroll 是在 window 上的
原理
300ms 浏览器支持程度
- 可以通过
meta
避免的(<meta name="viewport" content="width=device-width;initial-scale=1.0;maximum-scale=1.0; user-scalable=no;">
)- U4
- U3
- Chrome 33+
- WKWebView
- 必须依赖 FastClick 的 touch 机制
- UIWebView
WKWebView 检测
- 在支付宝内,检测
/\sWK\s/.test(navigator.userAgent)
- 在支付宝外,检测
window.webkit.messageHandlers
attachFastClick 干了什么
不需要的
对可以通过 meta 避免的浏览器
FastClick
原生即支持 U3, U4, Chrome 32+- 对于
WKWebView
, 在FastClick@1.0.6
源码的基础上,重写了notNeeded
函数,保证在支付宝钱包, Nebula容器
的WKWebView
内核下工作不需要使用FastClick
的机制,使用原生 click 事件
必须的
对必须依赖 FastClick 的 touch 机制的浏览器,目前仅针对
UIWebView
- 对于
UIWebView
,在FastClick@1.0.6
源码的基础上,重写了prototype.sendClick
方法,当FastClick
检测到touch
事件,认为应该发送click
时,拦截掉,根据一定逻辑,判断是否需要放行/阻止该模拟 click 事件
判断逻辑
UIWebView
根据以下scroll
机制分析,如果把touchstart
,touchend
,scroll:window
,scroll:target
事件按照流的形式记录下来,可以有以下规律,只要检测事件流是否符合特定规律,符合即阻止当次 click 即可
st
表示scroll:target
sw
表示scroll:window
a
表示touchstart
e
表示touchend
对于在 target 上的 scroll
- 滑动,无惯性,等停止点击:
- a-s-s-s-e----a-e ,返回 false
- 滑动,惯性,等停止点击: 2. a-s-s-s-e---s----a-e,返回 false
- 滑动,惯性,点击停止: 3. a-s-s-s-e-5-s----a-20-s-40-e 4. a-s-s-s-e-6-s----a-24-s-4-e 5. a-s-s-s-e-----a-17-s-8-e 6. a-s-s-s-e-----a-26-e-3-s 7. a-s-s-s-e----s-1-a---e 8. a-e-------a-s-e 返回 true
检测到Case 1,2
认为是正常点击,检测到Case 3,4,5,6,7
认为是滑动之后停止滑动,需要阻止掉
对于在 window 上的 scroll
- 滑动,无惯性,等停止点击:
- a-e-8-s---a-e ,返回 false
- 滑动,惯性,等停止点击: 2. a-e---s----a-e,返回 false
- 滑动,惯性,点击停止: 3. a-e----a-e-s 4. a-e------s-a-e-s 5. a-s-e-------a-e-s 6. a-s-e-------a-s-e-s 返回 true
检测到Case 1,2
认为是正常点击,检测到Case 3,4,5,6
认为是滑动之后停止滑动,需要阻止掉
UIWebView 的 Scroll 机制
根据https://lark.alipay.com/hybrid/dev/uiwebview-stop-js该文档,分别对应着 2 种 scroll 方式,分别是,在 window 上做滚动,还有一种是在某个子元素上开启overflow: scroll
做滚动,两种方式下的处理不太一致。
window
- 手指按下滑动期间
js 不执行,scroll 事件不触发
手指放开
- 触发惯性滚动
惯性滚动期间js不执行,scroll事件不触发
惯性滚动结束时,js 执行,scroll 事件触发一次
- 不触发惯性滚动,直接停止
停止时开始执行 js,scroll 事件触发一次
target(子元素)
- 手指按下滑动期间
js 执行
scroll 事件触发(间隔典型值 10-30ms)(如果手指不移动不触发,必须手指移动)
手指放开
- 触发惯性滚动(需要通过
-webkit-overflow-scrolling: touch;
开启)
js 执行
scroll 在惯性滚动停止时触发一次
- 不触发惯性滚动,直接停止
一切正常,由于上述 scroll 触发正常,此时不会特地触发一次 scroll
- 触发惯性滚动(需要通过