From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- files/zh-cn/web/api/touch_events/index.html | 353 +++++++++++++++++++++ .../multi-touch_interaction/index.html | 258 +++++++++++++++ .../index.html | 65 ++++ .../api/touch_events/using_touch_events/index.html | 151 +++++++++ 4 files changed, 827 insertions(+) create mode 100644 files/zh-cn/web/api/touch_events/index.html create mode 100644 files/zh-cn/web/api/touch_events/multi-touch_interaction/index.html create mode 100644 files/zh-cn/web/api/touch_events/supporting_both_touchevent_and_mouseevent/index.html create mode 100644 files/zh-cn/web/api/touch_events/using_touch_events/index.html (limited to 'files/zh-cn/web/api/touch_events') diff --git a/files/zh-cn/web/api/touch_events/index.html b/files/zh-cn/web/api/touch_events/index.html new file mode 100644 index 0000000000..50da7204f6 --- /dev/null +++ b/files/zh-cn/web/api/touch_events/index.html @@ -0,0 +1,353 @@ +--- +title: 触摸事件 +slug: Web/API/Touch_events +tags: + - DOM + - Touch Event + - touch + - 事件 + - 指南 + - 移动设备 + - 触控 + - 触摸 + - 触摸事件 + - 触摸屏 触控板 +translation_of: Web/API/Touch_events +--- +
{{DefaultAPISidebar("Touch Events")}}
+ +
+ +

为了给基于触摸的用户界面提供高质量的支持,触摸事件提供了在触摸屏或触控板上解释手指(或触控笔)活动的能力。

+ +
+ +

触摸事件接口是较为底层的 API,可为特定程序提供多点触控交互(比如双指手势)的支持。多点触控交互开始于一个手指(或触控笔)开始接触设备平面的时刻。随后其他手指也可触摸设备表面,并随意进行划动。当所有手指离开设备平面时,交互结束。整个交互期间,程序接收开始、移动、结束三个阶段的触摸事件。

+ +

触摸事件与鼠标事件类似,不同的是触摸事件还提供同一表面不同位置的同步触摸。{{domxref("TouchEvent")}} 接口将当前所有活动的触摸点封装起来。{{domxref("Touch")}} 接口表示单独一个触摸点,其中包含参考浏览器视角的相对坐标。

+ +

定义

+ +
+
表面(surface)
+
可感知触摸的平面,可以是屏幕或触控板。
+
+ +
+
触摸点(touch point)
+
表面上的一个接触点.。有可能是手指 (或者胳膊肘、耳朵、鼻子都行。但一般是手指) 或者触摸笔.
+
+ +

接口

+ +
+
{{ domxref("TouchEvent") }}
+
表示位于表面上的一个触摸点的某个状态发生改变时产生的事件。
+
{{ domxref("Touch") }}
+
表示用户与触摸表面间的一个单独的接触点。
+
{{ domxref("TouchList") }}
+
表示一组 Touch,用于多点触控的情况。
+
+ +

示例

+ +

本示例通过对多个触控点进行同步跟踪,让用户通过多点触控的方式在 {{ HTMLElement("canvas") }} 元素上用两个(或以上)手指同时画图。本示例只在支持触摸事件的浏览器下生效。

+ +
注:下文中用“手指”表示与平面的交互,当然触控笔等也是可行的。
+ +

创建画布

+ +
<canvas id="canvas" width="600" height="600" style="border:solid black 1px;">
+  你的浏览器不支持 canvas 元素。
+</canvas>
+<br>
+日志:<pre id="log" style="border: 1px solid #ccc;"></pre>
+ +

设置事件处理器

+ +

当页面加载时,下文的 startup() 函数由 window.onload 属性触发。

+ +
window.onload = function startup() {
+  const el = document.getElementsByTagName("canvas")[0];
+  el.addEventListener("touchstart", handleStart, false);
+  el.addEventListener("touchend", handleEnd, false);
+  el.addEventListener("touchmove", handleMove, false);
+  log("初始化成功。")
+}
+
+ +

该函数为 {{ HTMLElement("canvas") }} 元素设置了所有相关的事件监听器,使事件在触发时能够得到处理。

+ +

跟踪新触摸

+ +

我们将跟踪当前存在的所有触摸点。

+ +
const ongoingTouches = [];
+ +

当 {{event("touchstart")}} 事件触发时,平面上即出现一个新的触摸点,继而调用 handleStart() :

+ +
function handleStart(evt) {
+  evt.preventDefault();
+  console.log("触摸开始。");
+  const el = document.getElementsByTagName("canvas")[0];
+  const ctx = el.getContext("2d");
+  const touches = evt.changedTouches;
+
+  for (let i = 0; i < touches.length; i++) {
+    console.log("开始第 " + i + " 个触摸 ...");
+    ongoingTouches.push(copyTouch(touches[i]));
+    const color = colorForTouch(touches[i]);
+    ctx.beginPath();
+    ctx.arc(touches[i].pageX, touches[i].pageY, 4, 0, 2 * Math.PI, false);
+    // 在起点画一个圆。
+    ctx.fillStyle = color;
+    ctx.fill();
+    console.log("第 " + i + " 个触摸已开始。");
+  }
+}
+
+ +

 {{ domxref("event.preventDefault()") }} 阻止了浏览器继续处理触摸(和鼠标)事件。 然后我们取得上下文,从事件的 {{ domxref("TouchEvent.changedTouches") }} 属性中获得已改变的触摸点列表。

+ +

上述列表中所有的 {{ domxref("Touch") }} 对象即为当前所有活动的触摸点,把它们置于一个数组中,然后为每个触摸绘制起点。我们设置线条宽度为四像素,所以恰好会绘制一个半径为 4 像素的圆。

+ +

当触摸移动时绘制

+ +

在触摸平面上移动一根或者几根手指会触发 {{event("touchmove")}} 事件,从而将调用handleMove() 函数。本例中这个函数用于更新触摸点信息,并为每个触摸点从之前位置到当前位置之间绘制直线。

+ +
function handleMove(evt) {
+  evt.preventDefault();
+  const el = document.getElementsByTagName("canvas")[0];
+  const ctx = el.getContext("2d");
+  const touches = evt.changedTouches;
+
+  for (let i = 0; i < touches.length; i++) {
+    const color = colorForTouch(touches[i]);
+    const idx = ongoingTouchIndexById(touches[i].identifier);
+
+    if (idx >= 0) {
+      log("继续第 " + idx + "个触摸。");
+      ctx.beginPath();
+      log("ctx.moveTo(" + ongoingTouches[idx].pageX + ", " + ongoingTouches[idx].pageY + ");");
+      ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
+      log("ctx.lineTo(" + touches[i].pageX + ", " + touches[i].pageY + ");");
+      ctx.lineTo(touches[i].pageX, touches[i].pageY);
+      ctx.lineWidth = 4;
+      ctx.strokeStyle = color;
+      ctx.stroke();
+
+      ongoingTouches.splice(idx, 1, copyTouch(touches[i])); // 切换触摸信息
+      console.log(".");
+    } else {
+      log("无法确定下一个触摸点。");
+    }
+  }
+}
+
+ +

这个函数不仅对所有已改变的触摸点进行了遍历,还从缓存的触摸信息数组中得到了每个触摸点要绘制的新线段的起点。这是通过读取每个触摸点的 {{domxref("Touch.identifier")}} 属性实现的。对每个触摸点而言,该属性是个唯一的整数,且手指接触表面的整个过程中,这个属性保持不变。

+ +

这样我们就可以取得每个触摸点之前位置的坐标,并且使用恰当的上下文方法绘制片段,从而将新旧两个位置连接起来。

+ +

当片段绘制完毕后,我们调用 Array.splice() 将 ongoingTouches 数组中触摸点之前的信息替换为当前信息。

+ +

触摸结束处理

+ +

用户的手指从表面抬起时将触发 {{event("touchend")}} 事件。我们通过调用下面的 handleEnd() 函数来处理此类事件。 这个函数的工作就是为每个结束的触摸点绘制最后一个片段,然后将触摸点从 ongoingTouches 数组中移除。

+ +
function handleEnd(evt) {
+  evt.preventDefault();
+  log("触摸结束。");
+  const el = document.getElementsByTagName("canvas")[0];
+  const ctx = el.getContext("2d");
+   touches = evt.changedTouches;
+
+  for (let i = 0; i < touches.length; i++) {
+    const color = colorForTouch(touches[i]);
+    const idx = ongoingTouchIndexById(touches[i].identifier);
+
+    if (idx >= 0) {
+      ctx.lineWidth = 4;
+      ctx.fillStyle = color;
+      ctx.beginPath();
+      ctx.moveTo(ongoingTouches[idx].pageX, ongoingTouches[idx].pageY);
+      ctx.lineTo(touches[i].pageX, touches[i].pageY);
+      ctx.fillRect(touches[i].pageX - 4, touches[i].pageY - 4, 8, 8);
+      // 在终点画一个正方形
+      ongoingTouches.splice(idx, 1); // 用完后移除
+    } else {
+      log("无法确定要结束哪个触摸点。");
+    }
+  }
+}
+
+ +

这个函数与上一个很相像,唯一的实质性区别就是在调用 Array.splice() 时, 我们把用过的触摸实体从 ongoingTouches 数组中直接移除了,不再添加更新信息。对这个触摸点的跟踪随之终止。

+ +

触摸取消处理

+ +

如果用户的手指不小心滑入浏览器界面,或者触摸需要取消时,会触发 {{event("touchcancel")}} 事件。从而会调用下面的 handleCancel() 函数:

+ +
function handleCancel(evt) {
+  evt.preventDefault();
+  console.log("触摸取消。");
+  const touches = evt.changedTouches;
+
+  for (let i = 0; i < touches.length; i++) {
+    const idx = ongoingTouchIndexById(touches[i].identifier);
+    ongoingTouches.splice(idx, 1); // 用完后移除
+  }
+}
+
+ +

这里的想法是立刻结束触摸,所以我们直接从 ongoingTouches 数组中将其移除,而不去绘制最后的片段。

+ +

便捷函数

+ +

本例中使用了几个便捷函数,有必要简单了解一下,对理解其它代码很有帮助。

+ +

为每个触摸点选择一个颜色

+ +

为了区分每个触摸点绘制的内容,我们引入 colorForTouch() 函数,根据每个触摸点所独有的标记设定一个颜色。 这个标记在这里可能是一个无意义的数字,但我们至少可以利用它“对于每个触摸点的值都不同”这一特性。

+ +
function colorForTouch(touch) {
+  let r = touch.identifier % 16;
+  let g = Math.floor(touch.identifier / 3) % 16;
+  let b = Math.floor(touch.identifier / 7) % 16;
+  r = r.toString(16); // 转换为十六进制字符串
+  g = g.toString(16); // 转换为十六进制字符串
+  b = b.toString(16); // 转换为十六进制字符串
+  const color = "#" + r + g + b;
+  log("identifier " + touch.identifier + " 触摸的颜色为:" + color);
+  return color;
+}
+
+ +

这个函数返回一个表示颜色的字符串,可以在调用 {{ HTMLElement("canvas") }} 的函数时使用。比如,若 {{ domxref("Touch.identifier") }} 的值为 10, 则返回的字符串为 "#aaa"。

+ +
+

译注:这里的 colorForTouch() 不是一个好主意。Touch.identifier 是一个实验性属性,存在兼容性问题,不同浏览器之间实现方法不同,因此会得到不同的结果。

+
+ +

拷贝触摸对象

+ +

有些浏览器(比如 mobile Safari)会在不同事件之间复用触摸点对象,因此只复制所需的部分,要优于引用整个对象。

+ +
function copyTouch(touch) {
+  return {
+    identifier: touch.identifier,
+    pageX: touch.pageX,
+    pageY: touch.pageY
+  };
+}
+ +

查找触摸点

+ +

ongoingTouchIndexById() 函数通过遍历 ongoingTouches 数组来寻找与给定标记相匹配的触摸点,返回该触摸点在数组中的下标。

+ +
function ongoingTouchIndexById(idToFind) {
+  for (let i = 0; i < ongoingTouches.length; i++) {
+    const id = ongoingTouches[i].identifier;
+
+    if (id == idToFind) {
+      return i;
+    }
+  }
+  return -1;    // 未找到
+}
+
+ +

显示后台操作记录

+ +
function log(msg) {
+  const p = document.getElementById('log');
+  p.innerHTML = msg + "\n" + p.innerHTML;
+}
+ +

如果你的浏览器支持触摸,就可以 在线试用

+ +

jsFiddle 上的示例

+ +

附加信息

+ +

本节提供了在 web 应用中处理触摸事件的其它信息。

+ +

处理点击

+ +

由于在 {{event("touchstart")}}(或系列 {{event("touchmove")}} 事件里的首个)中调用 preventDefault() 也会阻止相应的鼠标事件的触发,因此一般情况下我们在{{event("touchmove")}} 而不是 {{event("touchstart")}} 中调用它,这样,鼠标事件仍可正常触发,链接等部件也可继续工作。有些框架采取了一个替代方案,使用触摸事件代替鼠标事件来达到相同目的。 (下面的示例过于简单,可能产生奇怪的行为。这里仅仅作为一个引导。)

+ +
function onTouch(evt) {
+  evt.preventDefault();
+  if (evt.touches.length > 1 || (evt.type == "touchend" && evt.touches.length > 0))
+    return;
+
+  const newEvt = document.createEvent("MouseEvents");
+  let type = null;
+  let touch = null;
+
+  switch (evt.type) {
+    case "touchstart":
+      type = "mousedown";
+      touch = evt.changedTouches[0];
+      break;
+    case "touchmove":
+      type = "mousemove";
+      touch = evt.changedTouches[0];
+      break;
+    case "touchend":
+      type = "mouseup";
+      touch = evt.changedTouches[0];
+      break;
+  }
+
+  newEvt.initMouseEvent(type, true, true, evt.originalTarget.ownerDocument.defaultView, 0,
+    touch.screenX, touch.screenY, touch.clientX, touch.clientY,
+    evt.ctrlKey, evt.altKey, evt.shiftKey, evt.metaKey, 0, null);
+  evt.originalTarget.dispatchEvent(newEvt);
+}
+
+ +

只在第二次触摸时调用 preventDefault()

+ +

为了阻止页面产生类似 pinchZoom 的行为,可以通过“在系列触摸点的第二个调用 preventDefault()”技术来实现。但是该技术的行为并没有在触摸事件的标准中做出完整定义,并且在不同浏览器中会产生不同行为(比如,iOS将阻止缩放,但允许二指平移;Android 允许缩放但阻止平移;Opera 和 Firefox 目前会阻止所有缩放和平移操作)。目前,对于此类情况最好依靠 meta viewport 来阻止缩放,而不要依赖于上述行为。

+ +
+
+ +

标准

+ + + + + + + + + + + + + + + + + + + +
标准状态备注
{{SpecName('Touch Events 2', '#touch-interface', 'Touch')}}{{Spec2('Touch Events 2')}}添加 radiusXradiusYrotationAngle 和 force 属性
{{SpecName('Touch Events', '#touch-interface', 'Touch')}}{{Spec2('Touch Events')}}首次引入
+ +

浏览器兼容性

+ +

Touch

+ + + +

{{Compat("api.Touch")}}

+ +

Firefox,触摸事件以及多进程(e10s)

+ +

在 Firefox 中,触摸事件随 e10s(electrolysis 即 多进程 Firefox)的禁用而禁用。e10s 在 Firefox 中默认为可用,但可以在某些特定情形下关闭它,比如在安装一些要求禁用 e10s 的工具或扩展时。这意味着即使在支持触屏的桌面或便携设备上,触摸事件也可能失效。

+ +

你可以使用 about:support 查看“应用程序概要”部分中“多进程窗口”一栏来确定 e10s 是否启用。1/1 表示启用,0/1 表示禁用。

+ +

如果你想强制性的开启 e10s(来显式重新启用触摸事件支持),你需要使用 about:config 创建一个布尔类型的设置 browser.tabs.remote.force-enable,将它设置为 true,重启浏览器,e10s 将始终启用而不受其他设置的影响。

diff --git a/files/zh-cn/web/api/touch_events/multi-touch_interaction/index.html b/files/zh-cn/web/api/touch_events/multi-touch_interaction/index.html new file mode 100644 index 0000000000..3b8426c319 --- /dev/null +++ b/files/zh-cn/web/api/touch_events/multi-touch_interaction/index.html @@ -0,0 +1,258 @@ +--- +title: 多点触控交互 +slug: Web/API/Touch_events/Multi-touch_interaction +translation_of: Web/API/Touch_events/Multi-touch_interaction +--- +

{{DefaultAPISidebar("Touch Events")}}

+ +

The touch event interfaces support application-specific single and multi-touch interactions. However, the interfaces can be a bit tricky for programmers to use because touch events are very different from other DOM input events, such as {{domxref("MouseEvent","mouse events")}}. The application described in this guide shows how to use touch events for simple single and multi-touch interactions, the basics needed to build application-specific gestures.

+ +

A live version of this application is available on Github. The source code is available on Github and pull requests and bug reports are welcome.

+ +

Example

+ +

This example demonstrates using the {{event("touchstart")}}, {{event("touchmove")}}, {{event("touchcancel")}}, and {{event("touchend")}}) touch events for the following gestures: single touch, two (simultaneous) touches, more than two simultaneous touches, 1-finger swipe, and 2-finger move/pinch/swipe.

+ +

Define touch targets

+ +

The application uses {{HTMLElement("div")}} elements to represent four touch areas.

+ +
<style>
+  div {
+    margin: 0em;
+    padding: 2em;
+  }
+  #target1 {
+    background: white;
+    border: 1px solid black;
+  }
+  #target2 {
+    background: white;
+    border: 1px solid black;
+  }
+  #target3 {
+    background: white;
+    border: 1px solid black;
+  }
+  #target4 {
+    background: white;
+    border: 1px solid black;
+  }
+</style>
+
+ +

Global state

+ +

tpCache is used to cache touch points for processing outside of the event where they were fired.

+ +
// Log events flag
+var logEvents = false;
+
+// Touch Point cache
+var tpCache = new Array();
+
+ +

Register event handlers

+ +

Event handlers are registered for all four touch event types. The {{event("touchend")}} and {{event("touchcancel")}} event types use the same handler.

+ +
function set_handlers(name) {
+ // Install event handlers for the given element
+ var el=document.getElementById(name);
+ el.ontouchstart = start_handler;
+ el.ontouchmove = move_handler;
+ // Use same handler for touchcancel and touchend
+ el.ontouchcancel = end_handler;
+ el.ontouchend = end_handler;
+}
+
+function init() {
+ set_handlers("target1");
+ set_handlers("target2");
+ set_handlers("target3");
+ set_handlers("target4");
+}
+
+ +

Move/Pinch/Zoom handler

+ +

This function provides very basic support for 2-touch horizontal move/pinch/zoom handling. The code does not include error handling, or vertical moving. Note that the threshold for pinch and zoom movement detection is application specific (and device dependent).

+ +
// This is a very basic 2-touch move/pinch/zoom handler that does not include
+// error handling, only handles horizontal moves, etc.
+function handle_pinch_zoom(ev) {
+
+ if (ev.targetTouches.length == 2 && ev.changedTouches.length == 2) {
+   // Check if the two target touches are the same ones that started
+   // the 2-touch
+   var point1=-1, point2=-1;
+   for (var i=0; i < tpCache.length; i++) {
+     if (tpCache[i].identifier == ev.targetTouches[0].identifier) point1 = i;
+     if (tpCache[i].identifier == ev.targetTouches[1].identifier) point2 = i;
+   }
+   if (point1 >=0 && point2 >= 0) {
+     // Calculate the difference between the start and move coordinates
+     var diff1 = Math.abs(tpCache[point1].clientX - ev.targetTouches[0].clientX);
+     var diff2 = Math.abs(tpCache[point2].clientX - ev.targetTouches[1].clientX);
+
+     // This threshold is device dependent as well as application specific
+     var PINCH_THRESHHOLD = ev.target.clientWidth / 10;
+     if (diff1 >= PINCH_THRESHHOLD && diff2 >= PINCH_THRESHHOLD)
+         ev.target.style.background = "green";
+   }
+   else {
+     // empty tpCache
+     tpCache = new Array();
+   }
+ }
+}
+
+ +

Touch start handler

+ +

The {{event("touchstart")}} event handler caches touch points to support 2-touch gestures. It also calls {{domxref("Event.preventDefault","preventDefault()")}} to keep the browser from applying further event handling (for example, mouse event emulation).

+ +
function start_handler(ev) {
+ // If the user makes simultaneious touches, the browser will fire a
+ // separate touchstart event for each touch point. Thus if there are
+ // three simultaneous touches, the first touchstart event will have
+ // targetTouches length of one, the second event will have a length
+ // of two, and so on.
+ ev.preventDefault();
+ // Cache the touch points for later processing of 2-touch pinch/zoom
+ if (ev.targetTouches.length == 2) {
+   for (var i=0; i < ev.targetTouches.length; i++) {
+     tpCache.push(ev.targetTouches[i]);
+   }
+ }
+ if (logEvents) log("touchStart", ev, true);
+ update_background(ev);
+}
+
+ +

Touch move handler

+ +

The {{event("touchmove")}} handler calls {{domxref("Event.preventDefault","preventDefault()")}} for the same reason mentioned above, and invokes the pinch/zoom handler.

+ +
function move_handler(ev) {
+ // Note: if the user makes more than one "simultaneous" touches, most browsers
+ // fire at least one touchmove event and some will fire several touchmoves.
+ // Consequently, an application might want to "ignore" some touchmoves.
+ //
+ // This function sets the target element's border to "dashed" to visually
+ // indicate the target received a move event.
+ //
+ ev.preventDefault();
+ if (logEvents) log("touchMove", ev, false);
+ // To avoid too much color flashing many touchmove events are started,
+ // don't update the background if two touch points are active
+ if (!(ev.touches.length == 2 && ev.targetTouches.length == 2))
+   update_background(ev);
+
+ // Set the target element's border to dashed to give a clear visual
+ // indication the element received a move event.
+ ev.target.style.border = "dashed";
+
+ // Check this event for 2-touch Move/Pinch/Zoom gesture
+ handle_pinch_zoom(ev);
+}
+
+ +

Touch end handler

+ +

The {{event("touchend")}} handler restores the event target's background color back to its original color.

+ +
function end_handler(ev) {
+  ev.preventDefault();
+  if (logEvents) log(ev.type, ev, false);
+  if (ev.targetTouches.length == 0) {
+    // Restore background and border to original values
+    ev.target.style.background = "white";
+    ev.target.style.border = "1px solid black";
+  }
+}
+
+ +

Application UI

+ +

The application uses {{HTMLElement("div")}} elements for the touch areas and provides buttons to enable logging and clear the log.

+ +
<div id="target1"> Tap, Hold or Swipe me 1</div>
+<div id="target2"> Tap, Hold or Swipe me 2</div>
+<div id="target3"> Tap, Hold or Swipe me 3</div>
+<div id="target4"> Tap, Hold or Swipe me 4</div>
+
+<!-- UI for logging/bebugging -->
+<button id="log" onclick="enableLog(event);">Start/Stop event logging</button>
+<button id="clearlog" onclick="clearLog(event);">Clear the log</button>
+<p></p>
+<output></output>
+
+ +

Miscellaneous functions

+ +

These functions support the application but aren't directly involved with the event flow.

+ +

Update background color

+ +

The background color of the touch areas will change as follows: no touch is white; one touch is yellow; two simultaneous touches is pink, and three or more simultaneous touches is lightblue. See touch move for information about the background color changing when a 2-finger move/pinch/zoom is detected.

+ +
function update_background(ev) {
+ // Change background color based on the number simultaneous touches
+ // in the event's targetTouches list:
+ //   yellow - one tap (or hold)
+ //   pink - two taps
+ //   lightblue - more than two taps
+ switch (ev.targetTouches.length) {
+   case 1:
+     // Single tap`
+     ev.target.style.background = "yellow";
+     break;
+   case 2:
+     // Two simultaneous touches
+     ev.target.style.background = "pink";
+     break;
+   default:
+     // More than two simultaneous touches
+     ev.target.style.background = "lightblue";
+ }
+}
+
+ +

Event logging

+ +

The functions are used to log event activity to the application window, to support debugging and learning about the event flow.

+ +
function enableLog(ev) {
+  logEvents = logEvents ? false : true;
+}
+
+function log(name, ev, printTargetIds) {
+  var o = document.getElementsByTagName('output')[0];
+  var s = name + ": touches = " + ev.touches.length +
+                " ; targetTouches = " + ev.targetTouches.length +
+                " ; changedTouches = " + ev.changedTouches.length;
+  o.innerHTML += s + "
+";
+
+  if (printTargetIds) {
+    s = "";
+    for (var i=0; i < ev.targetTouches.length; i++) {
+      s += "... id = " + ev.targetTouches[i].identifier + "
+";
+    }
+    o.innerHTML += s;
+  }
+}
+
+function clearLog(event) {
+ var o = document.getElementsByTagName('output')[0];
+ o.innerHTML = "";
+}
+
+ + + + diff --git a/files/zh-cn/web/api/touch_events/supporting_both_touchevent_and_mouseevent/index.html b/files/zh-cn/web/api/touch_events/supporting_both_touchevent_and_mouseevent/index.html new file mode 100644 index 0000000000..99f5ee55f7 --- /dev/null +++ b/files/zh-cn/web/api/touch_events/supporting_both_touchevent_and_mouseevent/index.html @@ -0,0 +1,65 @@ +--- +title: 同时支持触屏事件和鼠标事件 +slug: Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent +translation_of: Web/API/Touch_events/Supporting_both_TouchEvent_and_MouseEvent +--- +

{{DefaultAPISidebar("Touch Events")}}

+ +

{{domxref("Touch_events","touch")}} 接口使得应用可以提高触屏设备上的用户体验。然而,现在绝大多数的web内容都是为鼠标操作而设计的。因此,即使浏览器支持触屏,也必须要模拟(emulate)鼠标事件,这样即使是那些只能接受鼠标输入的内容,也不需要进行额外修改就可以正常工作。

+ +

理想状态下,一个基于触屏的应用不需要去明确指定鼠标输入。然而,由于浏览器必须要模拟(emulate)鼠标事件,很有可能有一些特定的交互问题需要去处理。下面列出了这些交互的细节 ,并指导应用开发者该如何去处理它们。

+ +

事件触发

+ +

触摸事件标准定义了一些关于触摸和鼠标交互的浏览器要求(有关详细信息,请参阅与鼠标事件的交互和单击部分),注意浏览器可以触发触摸事件和鼠标事件以响应相同的用户输入。本节介绍可能影响应用程序的条件。

+ +

如果浏览器因单个用户输入而触发触摸和鼠标事件,则浏览器必须在任何鼠标事件之前触发{{event("touchstart")}}。因此,如果应用程序不希望在特定触摸{{domxref("Touch.target","target")}} 元素上触发鼠标事件,则元素的触摸事件处理程序应调用{{domxref("Event.preventDefault()","preventDefault()")}}并且不会调度其他鼠标事件。

+ +

这是{{event("touchmove")}}事件处理程序调用的代码片段

+ +

preventDefault().

+ +
// touchmove handler
+function process_touchmove(ev) {
+  // Call preventDefault() to prevent any further handling
+  ev.preventDefault();
+}
+
+ +

事件顺序

+ +

虽然触摸和鼠标事件的特定顺序是根据实际情况而定的,但标准表明事件执行顺序是固定的:对于单个输入:

+ + + +

如果 {{event("touchstart")}}, {{event("touchmove")}} 或者 {{event("touchend")}} 在触摸的过程中触发了touchcancel事件,后面的鼠标事件将不会被触发,由此产生的事件序列只是:

+ + + +

社区

+ + + +

相关主题和资源

+ + diff --git a/files/zh-cn/web/api/touch_events/using_touch_events/index.html b/files/zh-cn/web/api/touch_events/using_touch_events/index.html new file mode 100644 index 0000000000..a9a643f82d --- /dev/null +++ b/files/zh-cn/web/api/touch_events/using_touch_events/index.html @@ -0,0 +1,151 @@ +--- +title: 使用触摸事件 +slug: Web/API/Touch_events/Using_Touch_Events +translation_of: Web/API/Touch_events/Using_Touch_Events +--- +

{{DefaultAPISidebar("Touch Events")}}

+ +

今天,大多数 Web 内容是为键盘和鼠标输入而设计的。然而,具有触摸屏(特别是便携式设备)的设备是主流的,Web应用程序可以使用 {{domxref("Touch_events","Touch Events")}} 直接处理基于触摸的输入,或者应用程序可以使用可解释的鼠标事件以处理应用程序的输入。使用鼠标事件的缺点是它们不支持并发用户输入,而触摸事件支持多个同时输入(可能在触摸面上的不同位置),从而增强用户体验。

+ +

触摸事件界面支持应用程序特定的单触摸和多点触控交互,例如双指手势。当手指(或触控笔)首先触摸接触面时,多点触摸交互开始。其他手指随后可以触摸该表面并且可选地移动穿过该触摸表面。当手指从表面移除时,相互作用结束。在此交互期间,应用程序在开始,移动和结束阶段接收触摸事件。应用程序可以将其自己的语义应用于触摸输入。

+ +

Interfaces

+ +

触摸事件有三个接口 ({{domxref("Touch")}}, {{domxref("TouchEvent")}} 和 {{domxref("TouchList")}}) 和以下事件类型:

+ + + +

{{domxref("Touch")}} 接口表示触敏设备上的单个接触点。接触点通常被称为触摸点或仅仅是触摸点。触摸通常由触摸屏,笔或触控板上的手指或触控笔产生。触摸点的属性包括唯一标识符,触摸点的目标元素以及触摸点相对于视口,页面和屏幕的位置的X和Y坐标。

+ +

{{domxref("TouchList")}} 接口表示触摸表面上的触点的列表。因此,如果用户用一根手指触摸触控表面,则该列表将包含一个触点,并且如果用户用三个手指触摸该表面,则列表长度将为三个。

+ +

{{domxref("TouchEvent")}} 接口表示当触控屏上触点的状态发生改变时会发送的事件。当与触控屏开始接触时状态开始改变,移动触摸点且手指在触控屏上,释放触点然后退出触摸事件。这个接口的属性包括几个修饰键的状态(例如 shift键)和下面的触摸列表:

+ + + +

这些接口一起定义了相对较低级别的功能,但它们支持多种基于触摸的交互,包括熟悉的多点触控手势,如多指手指滑动,旋转,捏和缩放。

+ +

From interfaces to gestures

+ +

在定义手势的语义时,应用程序可能会考虑不同的因素。例如,当触摸结束时,触摸点从其起始位置行进到其位置的距离。另一个潜在因素是时间;例如,触摸开始和触摸结束之间经过的时间,或者用于创建双击手势的两个同时敲击之间的时间间隔。滑动的方向性(例如从左到右,从左到右等)是另一个要考虑的因素。

+ +

应用程序使用的触摸列表取决于应用程序手势的语义。例如,如果应用程序在一个元素上支持单一触摸(点击),则它将使用 {{event("touchstart")}} 中的 {{domxref("TouchEvent.targetTouches","targetTouches")}} 列表事件处理程序以特定应用程序处理触摸点。如果应用程序支持任意两个触摸点的双指滑动,它将使用 {{event("touchmove")}} 事件处理程序中的 {{domxref("TouchEvent.changedTouches","changedTouches")}} 列表确定两个触摸点是否已移动,然后以应用程序特定的方式实现该手势的语义。

+ +

当只有一个活动的触摸点时,浏览器通常会分派仿真的鼠标和点击事件。涉及两个或多个活动触摸点的多点触控交互通常只会产生触摸事件。为了防止模拟的鼠标事件被发送,请在触摸事件处理程序中使用 {{domxref("Event.preventDefault()","preventDefault()")}} 方法。有关鼠标和触摸事件之间的交互的详细信息,请参阅  {{domxref("Touch_events.Supporting_both_TouchEvent_and_MouseEvent", "Supporting both TouchEvent and MouseEvent")}}。

+ +

Basic steps

+ +

本节包含使用上述接口的基本用法。有关更详细的示例,请参阅 {{domxref("Touch_events","Touch Events Overview")}} 。

+ +

对每一个触摸事件类型注册一个事件处理器。

+ +
// Register touch event handlers
+someElement.addEventListener('touchstart', process_touchstart, false);
+someElement.addEventListener('touchmove', process_touchmove, false);
+someElement.addEventListener('touchcancel', process_touchcancel, false);
+someElement.addEventListener('touchend', process_touchend, false);
+ +

在事件处理程序中处理事件,实现应用程序的手势语义。

+ +
// touchstart handler
+function process_touchstart(ev) {
+  // Use the event's data to call out to the appropriate gesture handlers
+  switch (ev.touches.length) {
+    case 1: handle_one_touch(ev); break;
+    case 2: handle_two_touches(ev); break;
+    case 3: handle_three_touches(ev); break;
+    default: gesture_not_supported(ev); break;
+  }
+}
+ +

访问触摸点的属性。

+ +
// Create touchstart handler
+someElement.addEventListener('touchstart', function(ev) {
+  // Iterate through the touch points that were activiated
+  // for this element and process each event 'target'
+  for (var i=0; i < ev.targetTouches.length; i++) {
+    process_target(ev.targetTouches[i].target);
+  }
+}, false);
+ +

阻止游览器产生模拟鼠标事件。

+ +
// touchmove handler
+function process_touchmove(ev) {
+  // Set call preventDefault()
+  ev.preventDefault();
+}
+
+ +

Best practices

+ +

以下是使用触摸事件时要考虑的最佳做法:

+ + + +

Implementation and deployment status

+ +

touch events browser compatibility data 表明移动浏览器中的触摸事件支持相对较好,尽管其他实现正在进行中,桌面浏览器支持滞后。

+ +

关于触摸点的 touch area 的一些新功能 - 用户和触摸表面之间的接触面积正在被标准化。新功能包括最接近触摸点与触摸面的接触区域的椭圆的X和Y半径。接触点的旋转角度 - 应用于所描述的椭圆以与接触面积对准的旋转角度的数量也被标准化,以及触摸点上对屏幕的力量。

+ +

What about Pointer Events?

+ +

The introduction of new input mechanisms results in increased application complexity to handle various input events, such as key events, mouse events, pen/stylus events, and touch events. To help address this problem, the Pointer Events standard defines events and related interfaces for handling hardware agnostic pointer input from devices including a mouse, pen, touchscreen, etc.. That is, the abstract pointer creates a unified input model that can represent a contact point for a finger, pen/stylus or mouse.

+ +

The pointer event model can simplify an application's input processing since a pointer represents input from any input device. Additionally, the pointer event types are very similar to mouse event types (for example, pointerdown pointerup) thus code to handle pointer events closely matches mouse handling code.

+ +

The implementation status of pointer events in browsers is relatively low with IE11 and Edge having complete implementations. Firefox's implementation has been withdrawn because of {{bug("1166347")}}.

+ +

Examples and demos

+ +

The following documents describe how to use touch events and include example code:

+ + + +

Touch event demonstrations:

+ + + +

Community

+ + + + + + -- cgit v1.2.3-54-g00ecf