移动端 touch
原文链接:http://caibaojian.com/mobile-touch-event.html
本文主要介绍 TouchEvent 相关的一些对象与属性如 Touch, TouchList, touhces, targetTouches 等,以及使用的注意点和误区。
触摸事件有以下几种类型:touchstart
,touchmove
,touchend
这三种用的比较多,还有不常用的touchcancel
事件。当然 MDN上还介绍了touchenter
,touchleave
事件,具体适用的场景及兼容性如何还未做测试,感兴趣的可自行研究。
js中不同的事件类型,event
对象包含的属性也有所差异。我们先了解几个TouchEvent
涉及的对象。
提示:文中的demo都是在 chrome 模拟器,iPhone6s(iOS9.3.2) safari,ios微信上运行,安卓的兼容性未做测试·
Touch
Touch
对象代表一个触点,可以通过event.touches[0]获取,
每个触点包含位置,大小,形状,压力大小,和目标 element属性。
{
screenX: 511,
screenY: 400,//触点相对于屏幕左边沿的Y坐标
clientX: 244.37899780273438,
clientY: 189.3820037841797,//相对于可视区域
pageX: 244.37,
pageY: 189.37,//相对于HTML文档顶部,当页面有滚动的时候与clientX=Y 不等
force: 1,//压力大小,是从0.0(没有压力)到1.0(最大压力)的浮点数
identifier: 1036403715,//一次触摸动作的唯一标识符
radiusX: 37.565673828125, //能够包围用户和触摸平面的接触面的最小椭圆的水平轴(X轴)半径
radiusY: 37.565673828125,
rotationAngle: 0,//它是这样一个角度值:由radiusX 和 radiusY 描述的正方向的椭圆,需要通过顺时针旋转这个角度值,才能最精确地覆盖住用户和触摸平面的接触面
target: {} // 此次触摸事件的目标element
}
identifier
这个属性大家可能有疑惑,使用 Chrome 的模拟器发现多次触摸动作,值始终不变。用真机测试则不会有问题(我这里用的safari连接mac调试)。每次触摸包括start,move,end这整个过程,标志符都不变。下一次触摸动作开始,标志符就会变化。
screenY
clientY
在 safari 中 screenY
与clientY
值是相等的,在iOS微信中两个数值不等,但单位应该也不一样。
radiusX
radiusY
rotationAngle
测试过程中safari及微信内置浏览器都不支持这些属性,chrome模拟器可以。
TouchList
由Touch
对象构成的数组,通过event.touches
取到。一个Touch
对象代表一个触点,当有多个手指触摸屏幕时,TouchList
就会存储多个Touch
对象,前面说到的identifier
就用来区分每个手指对应的Touch
对象。
TouchEvent
TouchEvent
就是用来描述手指触摸屏幕的状态变化事件,除了一般DOM事件中event
对像具备的属性,还有一些特有的属性。
touches
一个TouchList
对象,包含当前所有接触屏幕的触点的Touch
对象,不论 touchstart 事件从哪个elment上触发。
targetTouches
也是一个TouchList
对象,包含了如下触点的 Touch 对象:touchstart从当前事件的目标element上触发
这里大家可能产生了疑惑,这两个对象到底有什么区别?尤其是我们使用chrome模拟器中运行 demo,打印两个对象发现他们其实是一样的。
这两个对象的区别可以类比event.target
与event.currentTarget
的区别,如果以前没留意,自行 js 高级程序设计。
我们先看一个 demo2,来了解 touch 事件的特性。
在线编辑: http://jsrun.net/3XKKp
预览地址: http://jsrun.net/rtd/3XKKp
大家进行以下两个操作,观察控制台发现了什么?
操作一:一根手指触摸蓝色box
,并滑动,继续滑动出蓝色box
操作二:一根手指触摸非蓝色box
区域,然后慢慢滑动到蓝色box
大家会发现:操作一中即使滑出蓝色
box
,而touchmove
,touchend
事件会继续触发,touches
,targetTouches
存储着相同的 Touch 对象,touchmove事件的目标元素仍然是box。
操作二中相关的 touch 事件都不会触发。很神奇的是 touchmove 事件,明明在 box 上滑动,却不会触发 touchmove 事件。
我们可以猜测,touch相关的事件是一个整体,一开始touchstart不可能被触发,则后续touch事件也不会被触发。当然你可以不监听 touchstart 事件,按照操作一 touchmove,touchend 还是可以触发的。
再看下面这个demo2
在线编辑:http://jsrun.net/XXKKp
访问地址:http://jsrun.net/rtd/XXKKp
这里我们对白色区域body
也添加了 touch 事件的监听,继续上述 demo1中的两个操作。
我们可以发现:
操作一可以发现:touch 相关的事件可以冒泡,触发了
box
,body
的touch事件。操作二只能触发 body 的touch 事件,和demo1同理。
我们可以观察下操作一的两个对象TouchEvent.targetTouches
,TouchEvent.touches
,无论是box
还是body
触发的 touch 事件,他们的存储的 Touch对象都是相同的,而且 target 都是 box。
接下来进行操作三:
用两根手指,一根手指触摸蓝色
box
,另一根触摸白色区域,然后滑动。
然后再次比较下targetTouches
和touches
,就可以发现他们的不同。
changedTouches
也是一个 TouchList 对象,对于 touchstart 事件, 这个 TouchList 对象列出在此次事件中新增加的触点。对于 touchmove 事件,列出和上一次事件相比较,发生了变化的触点。对于 touchend ,列出离开触摸平面的触点(这些触点对应已经不接触触摸平面的手指)。
touchend
这里要特别注意,touches和targetTouches只存储接触屏幕的触点,要获取触点最后离开的状态要使用changedTouches。
之前也经常用touches[0]
来获取Touch 对象,如果知道了 touches,targetTouches,changedTouches 的不同之处。在编写代码时可以更好的选择使用,程序也可以更严谨。
touch事件封装
(function () {
var coord={},
start={},
el; document.addEventListener('touchstart', touchStart);
document.addEventListener('touchmove',touchMove);
document.addEventListener('touchend',touchEnd);
document.addEventListener('touchcanel',touchCancel); function newEvent(type){
return new Event(type,{ bubbles: true,cancelable: true});
} function touchCancel () {
coord = {}
} function touchStart(e){
var c = e.touches[0];
start = {
x: c.clientX,
y: c.clientY,
time: Date.now()
};
el= e.target;
el='tagName' in el ? el : el.parentNode;
} function touchMove(e){
var t = e.touches[0];
coord = {
x: t.clientX - start.x,
y: t.clientY - start.y
}
} function touchEnd(){
var touchTimes = Date.now() - start.time,
c = 250 > touchTimes && Math.abs(coord.x) > 20 || Math.abs(coord.x) > 80,
s = 250 > touchTimes && Math.abs(coord.y) > 20 || Math.abs(coord.y) > 80,
left = coord.x < 0,
top = coord.y < 0;
if (250 > touchTimes && (isNaN(coord.y) || Math.abs(coord.y)) < 12 && (isNaN(coord.x) || Math.abs(coord.x) < 12)) {
el.dispatchEvent(newEvent('tap'));
}else if(750<touchTimes && (isNaN(coord.y) || Math.abs(coord.y)) < 12 && (isNaN(coord.x) || Math.abs(coord.x) < 12)){
el.dispatchEvent(newEvent('longTap'));
}
c ? el.dispatchEvent(left ? newEvent('swipeLeft') : newEvent('swipeRight')) : s && el.dispatchEvent(top ? newEvent('swipeUp') : newEvent('swipeDown')); coord={};
}
}());
touch.js()原生JS,支持tap,longTap,swipeLeft,swipeRight,SwipeTop,swipeDown事件。
移动项目开发过程中,经常需要用到滑动的事件来处理一些效果。通常情况下,我们会通过 touchstart->touchmove->touchend 的过程来定义这个事件。这些事件的触发顺序是 touchstart, touchmove, touchmove ….. touchend 。绝大部分平板或手机也正如我们想象的那样有序执行着。但是以Android 4.0.4为首的一些可恶分子却有些不听话:他们的touchend事件没有如预期的那样触发。
监听这些事件我们会发现,当只是轻点一下屏幕时,touchend可以正常触发。但是只要当 touchmove 被触发之后,touchend 就不会再被触发了,而且 touchmove 也没有持续触发。
在网上搜集了一些资料显示,这是 Android 上浏览器的bug
> On Android ICS if no preventDefault is called on touchstart or the firsttouchmove,
> further touchmove events and the touchend will not be fired.
正如提到的我们只需要在 touchstart 或者 touchmove 里执行一下 e.preventDefault(); 就可以避免这个bug。但是,问题来了:添加了 preventDefault 之后,正常的scroll事件也被屏蔽了!我们意外的发现滚动条也不能用了!
于是,我们开始尝试各种添加preventDefault事件的时机:闭包,延迟,判断等一一用上。最终焦点落在了firsttouchmove上,于是有了以下代码。
//code from http://caibaojian.com/mobile-touch-event.html
var touchY = 0;
$(this).on('touchstart', function(e){
var touch = e.touches[0];
touchY = touch.clientY;
}).on('touchmove', function(e){
var touch = e.touches[0]
if(Math.abs(touch.clientY - touchY) < 10){
e.preventDefault();
}
}).on('touchend', function(){
// 你的滑动事件
});
基本上主要的思想就是在 touchmove 的时候判断出纵轴的位移量,当小于某个值的时候就认为是在执行左右滑动,且需要执行 preventDefault 来确保 touchend 可以正常触发。当然,如果你有横向滚动条且想绑定上下滑动事件的话就需要自己修改一下代码。
touchEnd事件在Android的某些机子上兼容性不是很好,有些无法触发的bug,看一下这篇文章是怎么解决:彻底解决低端安卓手机touchend事件不触发(考虑scroll)
本次移动端开发时遇见了安卓4.2系统不能触发touchend的问题,有以下需求。
1. 横滑轮播图
2.下拉刷新页面内容
3.body滚动条不能失效
开始在轮播图touchmove事件中阻止了浏览器默认行为,此时touchend事件可以触发。
//拖拽轮播图
parentNode.addEventListener('touchmove',function(e) {
e.preventDefault();
})
然后复制了一份在下拉刷新事件中(此时下拉刷新也OK了)
//下拉刷新代码
document.addEventListener('touchmove', function(e) {
if (getTopDistance() <= 10) {
e.preventDefault();
}
});
不过此时新的问题又出来了,页面竟然不能上下滚动了,经过分析得出结论在document的touchmove事件中阻止了浏览器默认行为导致页面不能上下滑动。
最终参考了老外的一篇文章解决此问题。(横滑炒过7认为是拖拽录播图)
parentNode.addEventListener('touchmove',function(e) {
var _x = e.touches[0].pageX;
if((Math.abs(_x-parentNode.startX)>7)){
e.preventDefault();
}
e.stopPropagation();
})
下拉刷新时也加上判断条件决定是否阻止浏览器默认行为(竖直滚动超过10阻止浏览器默认行为)
document.addEventListener('touchmove', function(e) { if (getTopDistance() <= 10) {//当滚动条位置小于10
// alert('<');
var _x = e.touches[0].pageX;
var _y = e.touches[0].pageY; if (_y - obj.y > 10) {//滚动距离大于10
e.preventDefault(); } }
});
/*获得滚动条位置
*/
function getTopDistance() {
return document.documentElement.scrollTop || window.pageYOffset || document.body.scrollTop;
}
来源:前端开发博客
移动端 touch的更多相关文章
- H5案例分享:移动端touch事件判断滑屏手势的方向
移动端touch事件判断滑屏手势的方向 方法一 当开始一个touchstart事件的时候,获取此刻手指的横坐标startX和纵坐标startY: 当触发touchmove事件时,在获取此时手指的横坐标 ...
- 原生 JS 实现移动端 Touch 滑动反弹
什么是 Touch滑动?就是类似于 PC端的滚动事件,但是在移动端是没有滚动事件的,所以就要用到 Touch事件结合 js去实现,效果如下: 1. 准备工作 什么是移动端的 Touch事件?在移动端 ...
- 原生js移动端touch事件实现上拉加载更多
大家都知道jQuery里没有touch事件,所以在移动端使用原生js实现上拉加载效果还是很不错的,闲话不多说,代码如下: //获取要操作的元素 var objSection = document.ge ...
- 移动端 touch 事件的originalEvent
对于移动端的触摸事件,我们通过touchstart.touchmove.touchend实现,PC端一般使用mousedown.mousemove.mouseup实现. 我们获取事件坐标,原生js获取 ...
- 移动端touch事件影响click事件以及在touchmove添加preventDefault导致页面无法滚动的解决方法
这两天自己在写一个手机网页,用到了触屏滑动的特效,就是往右滑动的时候左侧隐藏的菜单从左边划出来. 做完之后在手机原生浏览器中运行正常,但在QQ和微信中打开,发现touchmove只会触发一次,而且to ...
- 移动端touch触屏滑动事件、滑动触屏事件监听!
一.触摸事件 ontouchstart.ontouchmove.ontouchend.ontouchcancel 目前移动端浏览器均支持这4个触摸事件,包括IE.由于触屏也支持MouseEvent,因 ...
- 移动端touch点穿(穿透)解决办法
回答一 穿透(点穿)是在mobile各种浏览器上发生的常见的bug.可能是由click事件的延迟或者事件冒泡导致. 移动web开发常用的Zepto库中的touch和tap事件就会有点穿的bug(Zep ...
- 移动端touch实现下拉刷新
移动端实现下拉刷新 第一部分:四个touch事件 1.touchstart:只要将手指放在了屏幕上(而不管是几只),都会触发touchstart事件. 2.touchmove: 当我们用手指在屏幕上滑 ...
- 移动端Touch事件基础
1.三个常用的移动端事件 ontouchstart 手指按下时触发 ontouchmove 手指移动时触发 ontouchend 手动抬起时触发 注意:这些事件当作事件属性使用时,不兼容谷歌浏览器. ...
随机推荐
- Qt解析xml
发现用 Qt 解析 xml 文件非常方便,下面是一个简单的解析 xml 文件的例子: #include <QtCore/QCoreApplication> #include <QDo ...
- FPGA三分频,五分频,奇数分频
我们在做FPGA设计时,有时会用到时钟频率奇数分频的频率,例如笔者FPGA的晶振为50M,当我们需要10M的时钟时,一种方式可以使用DCM或PLL获取,系统会内部分频到10M,但其实VERILOG内部 ...
- tomcat在conf/Catalina/localhost目录下配置项目路径
转自:http://wangyl93-dl-cn.iteye.com/blog/1508517 在tomcat的conf/Catalina/localhost目录下配置项目路径,tomcat启动是会直 ...
- Direct-X学习笔记--三维摄像机
一.介绍 哇! 到了传说中的3D摄像机啦! 之前我们写的东东,都是观察点不动,通过世界变换让东西动,今天,通过三维摄像机我们就能够改变我们的观察点,观察方向,任意在三维空间中驰骋.之前我们所设定的视角 ...
- 每日英语:Secrets Of Effective Office Humor
Margot Carmichael Lester loves making good-natured jokes at work. As owner of The Word Factory, a Ca ...
- python(28)获得网卡的IP地址,如何在其他文件夹中导入python模块
获得第几块网卡的ip地址: 如何在其他文件夹中导入模块 import sys sys.path.append('/search/chen/tool')#你的代码存放的目录 from Get_Ip im ...
- 队列——PowerShell版
继续读啊哈磊<啊哈!算法>感悟系列——队列 地铁售票处排队,先来的人先到队首先买完先走,后来的人排在队尾等候后买完后走. 想买票,必须排在队尾:买完票,只能从队首离开. 这种先进先出(Fi ...
- rhel7的xfs与Oracle database
在rhel7下,安装oracle database时,能够选择xfs文件,例如以下: Requirements for Installing Oracle Database 12.1on RHEL7 ...
- json关键总结
先引用 Newtonsoft.Json.Net20.dll //序列化对象的方法也非常的简单: string json = JsonConvert.SerializeObject(product); ...
- note,路况,计算
note,路况,计算