简介
智能手机和平板电脑等移动设备通常配备电容式触摸屏,用于捕获用户手指的互动操作。随着移动网络不断发展,支持越来越复杂的应用,网络开发者需要找到一种方法来处理这些事件。例如,几乎所有快节奏的游戏都需要玩家同时按下多个按钮,这对于触摸屏来说意味着多点触控。
Apple 在 iOS 2.0 中引入了 touch events API。Android 一直在追赶这一事实上的标准,并缩小差距。最近,一个 W3C 工作组正在合力制定此触摸事件规范。
在本文中,我将深入探讨 iOS 和 Android 设备以及支持触控的硬件上的桌面版 Chrome 提供的触摸事件 API,并探索您可以构建哪些类型的应用、介绍一些最佳实践,以及介绍有助于更轻松地开发支持触控的应用的实用技巧。
触摸事件
该规范中概述了三种基本触摸事件,并在移动设备上广泛实现:
- touchstart:手指放在一个 DOM 元素上。
- touchmove:手指沿着 DOM 元素拖动。
- touchend:从 DOM 元素移除手指。
每个触摸事件都包含三个触摸列表:
- touches:当前位于屏幕上的所有手指动作的列表。
- targetTouches:当前 DOM 元素上的手指列表。
- changedTouches:当前事件涉及的手指列表。例如,在 touchend 事件中,这将是移除的手指。
这些列表由包含触摸信息的对象组成:
- identifier:一个数字,用于唯一标识触摸会话中的当前手指。
- target:操作的目标 DOM 元素。
- 客户端/网页/屏幕坐标:操作发生在屏幕上的哪个位置。
- 半径坐标和 rotationAngle:描述近似手指形状的椭圆。
支持触控的应用
touchstart、touchmove 和 touchend 事件提供了一组足够丰富的功能,可支持几乎任何类型的触摸交互,包括所有常见的多点触控手势,如双指张合缩放、旋转等。
借助以下代码段,您可以使用单指轻触来拖动 DOM 元素:
var obj = document.getElementById('id');
obj.addEventListener('touchmove', function(event) {
// If there's exactly one finger inside this element
if (event.targetTouches.length == 1) {
var touch = event.targetTouches[0];
// Place element where the finger is
obj.style.left = touch.pageX + 'px';
obj.style.top = touch.pageY + 'px';
}
}, false);
以下是显示屏幕上所有当前触摸的示例。这只是为了让您了解设备的响应速度。
// Setup canvas and expose context via ctx variable
canvas.addEventListener('touchmove', function(event) {
for (var i = 0; i < event.touches.length; i++) {
var touch = event.touches[i];
ctx.beginPath();
ctx.arc(touch.pageX, touch.pageY, 20, 0, 2*Math.PI, true);
ctx.fill();
ctx.stroke();
}
}, false);
演示
许多有趣的多点触控演示已经出现,例如 Paul Ireland 和其他人制作的这个基于画布的绘图演示。
还有 Browser Ninja,这是一个技术演示,是一个使用 CSS3 转换和过渡以及画布功能的《水果忍者》克隆版:
最佳做法
禁止缩放
默认设置不适用于多点触控,因为您的滑动和手势通常与浏览器行为(如滚动和缩放)相关联。
如需停用缩放功能,请使用以下元标记将视口设置为不可供用户缩放:
<meta name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no>
如需详细了解如何设置视口,请参阅这篇有关移动 HTML5 的文章。
防止滚动
某些移动设备具有 touchmove 的默认行为,例如传统的 iOS 滚动回弹效果,这会导致视图在滚动超出内容边界时弹回。在许多多点触控应用中,这会造成混淆,并且可以轻松停用:
document.body.addEventListener('touchmove', function(event) {
event.preventDefault();
}, false);
谨慎渲染
如果您要编写的多点触控应用涉及复杂的多指手势,请注意您如何对触摸事件做出反应,因为您将一次处理这么多事件。请考虑上一部分中用于在屏幕上绘制所有触摸的示例。您可以在有触摸输入后立即绘制:
canvas.addEventListener('touchmove', function(event) {
renderTouches(event.touches);
}, false);
但这种方法不能随着屏幕上的手指数增加而扩展。 不过,您可以跟踪所有手指,并以循环方式进行渲染,以获得更出色的性能:
var touches = []
canvas.addEventListener('touchmove', function(event) {
touches = event.touches;
}, false);
// Setup a 60fps timer
timer = setInterval(function() {
renderTouches(touches);
}, 15);
利用 targetTouches 和 changedTouches
请注意,event.touches 是与屏幕接触的所有手指的数组,而不仅仅是 DOM 元素目标上的手指。您可能会发现,改用 event.targetTouches 或 event.changedTouches 会更有用。
最后,由于您要针对移动设备进行开发,因此应了解常见的移动设备最佳实践,这些实践在 Eric Bidelman 的文章以及此 W3C 文档中都有介绍。
设备支持
遗憾的是,轻触事件实现在完整性和质量方面差异很大。我编写了一个诊断脚本,用于显示有关触摸 API 实现的一些基本信息,包括支持哪些事件以及 touchmove 触发分辨率。我在 Nexus One 和 Nexus S 硬件上测试了 Android 2.3.3,在 Xoom 上测试了 Android 3.0.1,在 iPad 和 iPhone 上测试了 iOS 4.2。
简而言之,所有测试的浏览器都支持 touchstart、touchend 和 touchmove 事件。
规范中提供了另外 3 个触摸事件,但测试的所有浏览器都不支持这些事件:
- touchenter:移动的手指进入一个 DOM 元素。
- touchleave:移动的手指离开 DOM 元素。
- touchcancel:触摸操作被中断(具体取决于实现)。
在每个触摸列表中,被测浏览器还会提供 touches、targetTouches 和 changedTouches 触摸列表。不过,测试的浏览器都不支持 radiusX、radiusY 或 rotationAngle(它们指定了触摸屏幕的手指的形状)。
在触摸移动期间,所有测试设备上的事件触发频率大约为每秒 60 次。
Android 2.3.3(Nexus)
Android Gingerbread 浏览器(在 Nexus One 和 Nexus S 上测试)不支持多点触控。这是已知问题。
Android 3.0.1 (Xoom)
Xoom 的浏览器支持基本多点触控,但仅适用于单个 DOM 元素。浏览器无法正确响应对不同 DOM 元素的两次同时轻触。换句话说,以下内容会对两个同时发生的轻触操作做出响应:
obj1.addEventListener('touchmove', function(event) {
for (var i = 0; i < event.targetTouches; i++) {
var touch = event.targetTouches[i];
console.log('touched ' + touch.identifier);
}
}, false);
但以下内容不会:
var objs = [obj1, obj2];
for (var i = 0; i < objs.length; i++) {
var obj = objs[i];
obj.addEventListener('touchmove', function(event) {
if (event.targetTouches.length == 1) {
console.log('touched ' + event.targetTouches[0].identifier);
}
}, false);
}
iOS 4.x(iPad、iPhone)
iOS 设备完全支持多点触控,能够跟踪多点触控,并在浏览器中提供非常灵敏的触控体验。
开发者工具
在移动开发中,通常先在桌面设备上开始进行原型设计,然后再在您打算支持的设备上处理移动专用部分,这样更容易。多点触控功能就是在 PC 上难以测试的功能之一,因为大多数 PC 都没有触控输入功能。
必须在移动设备上进行测试可能会延长您的开发周期,因为您进行的每项更改都需要推送到服务器,然后加载到设备上。然后,应用运行后,您几乎无法调试应用,因为平板电脑和智能手机缺少 Web 开发者工具。
解决此问题的方法是在开发机上模拟触摸事件。对于单次轻触,可以根据鼠标事件模拟轻触事件。如果您有支持触控输入的设备(例如新型 Apple MacBook),则可以模拟多点触控事件。
单点触控事件
如果您想在桌面设备上模拟单点触控事件,Chrome 提供了开发者工具提供的触摸事件模拟功能。打开“开发者工具”,然后依次选择“设置”齿轮图标、“替换项”或“模拟”,并开启“模拟触摸事件”。
对于其他浏览器,您可以尝试使用 Phantom Limb,它可以模拟网页上的触摸事件,还可以提供一个巨大的手掌。
此外,还有 Touchable jQuery 插件,可统一跨平台的触摸和鼠标事件。
多点触控事件
为了让您的多点触控 Web 应用能够在多点触控触控板(例如 Apple MacBook 或 MagicPad)上的浏览器中运行,我创建了 MagicTouch.js polyfill。它会捕获触控板上的触摸事件,并将其转换为与标准兼容的触摸事件。
- 将 npTuioClient NPAPI 插件下载并安装到 ~/Library/Internet Plug-Ins/。
- 下载适用于 Mac 的 MagicPad 的 TongSeng TUIO 应用,然后启动服务器。
- 下载 MagicTouch.js,这是一个 JavaScript 库,用于根据 npTuioClient 回调模拟与规范兼容的触摸事件。
- 在应用中添加 magictouch.js 脚本和 npTuioClient 插件,如下所示:
<head>
...
<script src="/path/to/magictouch.js"></script>
</head>
<body>
...
<object id="tuio" type="application/x-tuio" style="width: 0px; height: 0px;">
Touch input plugin failed to load!
</object>
</body>
您可能需要启用该插件。
您可以访问 paulirish.com/demo/multi,查看使用 magictouch.js 的实时演示:
我仅在 Chrome 10 中测试了此方法,但只需进行一些细微调整,它应该也适用于其他新型浏览器。
如果您的计算机不支持多点触控输入,您可以使用其他 TUIO 追踪器(例如 reacTIVision)来模拟触摸事件。如需了解详情,请参阅 TUIO 项目页面。
请注意,您的手势可能与操作系统级多点触控手势相同。在 OS X 上,您可以前往“系统偏好设置”中的“触控板”偏好设置窗格,配置系统级事件。
随着移动浏览器对多点触控功能的支持越来越广泛,我非常高兴看到新的 Web 应用能够充分利用这款丰富的 API。