这篇翻译不完整。请帮忙从英语翻译这篇文章。
为了给触摸界面提供有力支持, 触摸事件提供了响应用户对触摸屏或者触摸板上操作的能力.
定义
- 平面
- 对触摸敏感的平面.可能是屏幕或者触控板.
- 触摸点
- 平面上的一个接触点. 有可能是手指 (或者 肘部, 耳朵, 鼻子, 或任何东西, 不过大多数情况下是手指) 或者触摸笔.
接口
TouchEvent
- 代表当触摸行为在平面上变化的时候发生的事件.
Touch
- 代表用户与触摸平面间的一个接触点.
TouchList
- 代表一系列的Touch; 一般在用户多个手指同时接触触控平面时使用这个接口.
DocumentTouch
- 包含了一些创建
Touch
对象与TouchList
对象的便捷方法.
例子
这个例子可跟踪多点同时触控,允许用户用多指触摸的方式在 <canvas>
元素上画图. 这个例子只会在支持触摸事件的浏览器下生效.
创建 canvas
<canvas id="canvas" width="600" height="600" style="border:solid black 1px;">
Your browser does not support canvas element.
</canvas>
<br>
<button onclick="startup()">Initialize</button>
<br>
Log: <pre id="log" style="border: 1px solid #ccc;"></pre>
设置事件处理器
当页面加载时,下面的startup()函数通过我们在<body>
元素上设置的onload
属性而被触发
.
function startup() { var el = document.getElementsByTagName("canvas")[0]; el.addEventListener("touchstart", handleStart, false); el.addEventListener("touchend", handleEnd, false); el.addEventListener("touchmove", handleMove, false); log("initialized.") }
这里给我们的 <canvas>
元素设置了所有触摸相关的事件监听器,因此当事件触发时我们就可以处理它们.
跟踪新的触摸行为
我们将检测正在进行的触摸事件
var ongoingTouches = new Array();
当一个 touchstart
事件被触发, 代表在触摸板上一个发生了一个新的触摸行为,下面的 handleStart()函数会被调用
.
function handleStart(evt) { evt.preventDefault(); var el = document.getElementsByTagName("canvas")[0]; var ctx = el.getContext("2d"); var touches = evt.changedTouches; for (var i=0; i<touches.length; i++) { ongoingTouches.push(touches[i]); var color = colorForTouch(touches[i]); ctx.fillStyle = color; ctx.fillRect(touches[i].pageX-2, touches[i].pageY-2, 4, 4); } }
event.preventDefault()
阻止了浏览器继续处理触摸事件 (这同样也阻止了鼠标事件的传递). 而后我们拿到事件上下文,从事件的TouchEvent.changedTouches
属性中拿到改变中的触摸点列表.
我们遍历上述的点列表Touch
并把这些点压入一个代表当前活动的触摸点组成的数组中,以这些点为起点画矩形; 我们设置线条宽度为四像素,所以最终我们画出来的是一个四乘四的正方形。
当触摸移动时绘制
每当一根或者几根手指在触摸平面上移动时, touchmove
事件被触发, 随之handleMove()函数被调用
.此例子中,这个函数更新了上面保存过的触摸点信息,之后,从触摸点之前的位置到现在的位置之间绘制直线,且对每个点都进行这样的操作.
function handleMove(evt) { evt.preventDefault(); var el = document.getElementsByTagName("canvas")[0]; var ctx = el.getContext("2d"); var touches = evt.changedTouches; ctx.lineWidth = 4; for (var i=0; i<touches.length; i++) { var color = colorForTouch(touches[i]); var idx = ongoingTouchIndexById(touches[i].identifier); ctx.fillStyle = color; ctx.beginPath(); ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY); ctx.lineTo(touches[i].pageX, touches[i].pageY); ctx.closePath(); ctx.stroke(); ongoingTouches.splice(idx, 1, touches[i]); // swap in the new touch record } }
这里同样遍历了所有被改变的触摸点,但为了决定每次新触摸要绘制的线段的起点,它也查询了我们先前缓存的触摸信息数组。这是通过查找每个触摸的 Touch.identifier
属性来做到的.这个属性是个整数,每次触摸都不同,在触摸事件期间手指一直接触表面,这个属性保持不变。
这样我们就可以拿到先前每个触摸的坐标点,之后以适当的上下文方法将两点连接起来,并绘制线段。
当这条线绘制完毕后我们调用 Array.splice()
,把ongoingTouches数组中之前的触摸点信息用现在的信息来代替.
对触摸行为的结束进行处理
当用户从触摸表面抬起手指时,touchend
事件被触发. 类似的当手指移除canvas区域外,我们会得到touchleave
事件. 我们利用相同的方式来处理这两种情况,即调用下面的handleEnd()函数
.这个函数的作用是给每个已经结束的触摸绘制最后一段线段,同时把这个触摸点从进行中的触摸列表数组中移除.
function handleEnd(evt) { evt.preventDefault(); var el = document.getElementsByTagName("canvas")[0]; var ctx = el.getContext("2d"); var touches = evt.changedTouches; ctx.lineWidth = 4; for (var i=0; i<touches.length; i++) { var color = colorForTouch(touches[i]); var idx = ongoingTouchIndexById(touches[i].identifier); ctx.fillStyle = color; ctx.beginPath(); ctx.moveTo(ongoingTouches[i].pageX, ongoingTouches[i].pageY); ctx.lineTo(touches[i].pageX, touches[i].pageY); ongoingTouches.splice(i, 1); // remove it; we're done } }
这个函数跟之前的函数很类似,唯一的区别是我们调用Array.splice()时
, 在正在进行的触摸列表中,我们仅仅将一个触摸的标识移除,而不再添加这个触摸新的信息。结果就是我们停止跟踪这个触摸点。
处理取消触摸事件
如果用户的手指滑出触摸区域,滑入浏览器界面时,或者触摸需要取消时,touchcancel
事件会被传递,下面的 handleCancel()
函数会被触发.
function handleCancel(evt) { evt.preventDefault(); var touches = evt.changedTouches; for (var i=0; i<touches.length; i++) { ongoingTouches.splice(i, 1); // remove it; we're done } }
因为我们的目的是立刻结束触摸,所以我们直接从正在进行的触摸列表中删除,不会绘制最后一部分线段。
便捷函数
这个例子使用了两个很方便的函数,有必要简单了解下这两个函数,会有助于更加清楚的理解代码剩余的部分。
为每次触摸选择一个颜色
为了让每次触摸绘制的内容看起来不相同,colorForTouch()函数用来根据每一次触摸所独有的标识来取颜色
. 这个标识的范围通常是0到所有活动触摸对象的数量-1. 而基本不可能会有人用多于16根手指去使用这个demo,我们直接把这种情况转为灰色。
function colorForTouch(touch) { var id = touch.identifier; id = id.toString(16); // make it a hex digit return "#" + id + id + id; }
这个函数返回一个字符串,可以用在 <canvas>
函数中用来设置绘制颜色. 举例来说,若触摸的标识符Touch.identifier
为10, 转换后的字符串为 "#aaa".
查询正在进行的触摸行为
下面的ongoingTouchIndexById()
函数通过遍历查找数组 ongoingTouches
来找到与给定标识相匹配的触摸行为,之后返回这个触摸行为在数组中的下标。
function ongoingTouchIndexById(idToFind) { for (var i=0; i<ongoingTouches.length; i++) { var id = ongoingTouches[i].identifier; if (id == idToFind) { return i; } } return -1; // not found }
附加小贴士
这部分提供了一些如何在web应用中处理触摸事件的小贴士。
处理点击
当处理一系列触摸操作时,为了阻止鼠标事件的触发,可以在touchstart
或第一个 touchmove中调用
preventDefault(),更适合的做法是在touchmove中调用preventDefault()。
这样做的话,鼠标事件仍然会被触发,相关的如链接等就可以继续工作。有些框架采取了一个替代方案,使用触摸事件代替鼠标事件来达到相同的目的。 (下面这个例子过于简单,也可能产生奇怪的行为。这里仅仅作为一个引导).
function onTouch(evt) { evt.preventDefault(); if (evt.touches.length > 1 || (evt.type == "touchend" && evt.touches.length > 0)) return; var newEvt = document.createEvent("MouseEvents"); var type = null; var touch = null; switch (event.type) { case "touchstart": type = "mousedown"; touch = event.changedTouches[[0]; case "touchmove": type = "mousemove"; touch = event.changedTouches[[0]; case "touchend": type = "mouseup"; touch = event.changedTouches[0]; } newEvt.initMouseEvent(type, true, true, event.originalTarget.ownerDocument.defaultView
, 0, touch.screenX, touch.screenY, touch.clientX, touch.clientY, evt.ctrlKey, evt.altKey, evt.shirtKey, evt.metaKey, 0, null); event.originalTarget
.dispatchEvent(newEvt); }
只在第二次触摸时调用 preventDefault()
有一种
技术可以在触摸时,阻止页面上发生pinchZoom(捏拉缩放)之类操作,他是通过在一系列触摸操作中,对第二个触摸调用
preventDefault()函数
. 这个行为未在标准中被定义,不同浏览器的表现也不同(IOS会阻止缩放但扔允许双指平移,Android会允许缩放,但阻止平移。这两种行为在Opera与Firefox中均被阻止。).因此现在不建议使用此方式, 而是通过meta viewport 来阻止页面缩放.
浏览器兼容性
Feature | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
Basic support | 22.0 | 18.0 (18.0) | 未实现 | 未实现 | 未实现 |
Feature | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
Basic support | (Yes) | ? | 6.0 (6.0) | ? | ? | (Yes) |
Gecko notes
偏好设置 dom.w3c_touch_events.enabled
可以用于打开或者禁用标准触摸事件。默认设置为打开。
早于Gecko 12.0 (Firefox 12.0 / Thunderbird 12.0 / SeaMonkey 2.9)内核版本的浏览器不支持多点触控事件,每次触摸事件发生是仅有一个触控点会报告。