多点触控网络开发

波利斯·斯姆斯
Boris Smus

简介

智能手机和平板电脑等移动设备通常具有电容式触控屏幕,用于捕获用户手指所做的互动。随着移动网络不断发展,使应用越来越复杂,Web 开发者需要找到一种方法来处理这些事件。例如,几乎所有快节奏游戏都需要玩家同时按下多个按钮,这对于触摸屏而言意味着多点触控。

Apple 在 iOS 2.0 中引入了触摸事件 API。Android 一直在赶上这一事实上的标准,并正在缩小差距。最近,一个 W3C 工作组正在一起制定此触摸事件规范

在本文中,我将深入探索 iOS 和 Android 设备提供的触摸事件 API,以及支持触摸的硬件上的桌面版 Chrome,探索您可以构建哪些类型的应用,介绍一些最佳实践,并介绍可让您更轻松地开发触摸式应用的实用技巧。

触摸事件

规范中列出了三种基本触摸事件,并在移动设备上广泛实现:

  • touchstart:手指放在一个 DOM 元素上。
  • touchmove:手指沿 DOM 元素拖动。
  • touchend:手指从 DOM 元素中移除。

每个触摸事件都包含三个触摸列表:

  • touches:当前位于屏幕上的所有手指的列表。
  • targetTouches:位于当前 DOM 元素上的手指的列表。
  • changedTouches:当前事件中涉及的手指的列表。例如,在 touchend 事件中,这指移开的手指。

这些列表由包含触摸信息的对象组成:

  • identifier:一个数字,用于唯一标识触摸会话中当前手指的手指。
  • target:作为操作目标的 DOM 元素。
  • 客户端/页面/屏幕坐标:操作在屏幕上发生的位置。
  • 半径坐标和 rotationAngle:描述与手指形状相似的椭圆形。

支持触摸的应用

touchstarttouchmovetouchend 事件提供了一组足够丰富的功能,可支持几乎任何类型的基于触摸的互动,包括所有常见的多点触控手势,如双指张合缩放、旋转等。

以下代码段可让您使用单指触摸拖动 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 Irish 等人制作的基于画布的绘图演示。

绘图屏幕截图

此外,Browser Ninja 是一个技术演示,演示了使用 CSS3 转换和过渡以及画布的《水果忍者》克隆应用:

浏览器高手

最佳实践

禁止缩放

默认设置不太适用于多点触控,因为您的滑动和手势通常与浏览器行为(例如滚动和缩放)相关联。

若要停用缩放功能,请使用以下元标记设置视口,使用户无法缩放:

<meta name="viewport" 
  content="width=device-width, initial-scale=1.0, user-scalable=no>

如需详细了解如何设置视口,请参阅这篇关于移动 HTML5 的文章

禁止滚动

某些移动设备具有触摸移动的默认行为,例如经典的 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 实现的一些基本信息,包括支持的事件以及触摸移动触发解决方案。我在 Nexus One 和 Nexus S 硬件上测试了 Android 2.3.3,在 Xoom 上测试了 Android 3.0.1,在 iPad 和 iPhone 上测试了 iOS 4.2。

简而言之,所有测试的浏览器都支持 touchstarttouchendtouchmove 事件。

该规范提供了另外三种触摸事件,但测试的浏览器均不支持这些事件:

  • touchenter:移动的手指进入一个 DOM 元素。
  • touchleave:移动的手指离开一个 DOM 元素。
  • touchcancel:触摸中断(特定于实现)。

在每个触摸列表中,被测试的浏览器还提供 touchestargetToucheschangedTouches 触摸列表。不过,测试的浏览器均不支持 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。它会从触控板捕获触摸事件,并将其转换为与标准兼容的触摸事件。

  1. 下载 npTuioClient NPAPI 插件并将其安装到 ~/Library/Internet plug-Ins/ 中。
  2. 下载适用于 Mac MagicPad 的 TongSeng TUIO 应用,并启动服务器。
  3. 下载 MagicTouch.js,这是一个 JavaScript 库,可基于 npTuioClient 回调来模拟符合规范的触摸事件。
  4. 在您的应用中添加 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。