前端工程师手册

触摸和鼠标在一起

简介

近三十年,桌面计算体验集中围绕在一个键盘与一个鼠标或轨迹板,以它们作为主要的用户输入设备。不过在过去十年间,智能手机和平板电脑带来了一个新的交互范式:触摸。通过引入触屏的Windows 8机器,以及现今很酷的触摸式Chromebook Pixel笔记本的发布,触摸现已成为桌面体验的部分预期。最大的挑战之一是创造出不仅仅工作于触摸设备和鼠标设备的体验,还要创造出用户同时使用这两种输入方法的体验——有时候是同时的!

这篇文章将帮助你理解触摸功能是如何内置于浏览器的,怎样将这种新的界面机制集成到你已有的应用,以及触摸怎样才可以和鼠标输入和谐共存。

Web平台中的触摸状态

iPhone是第一个在web浏览器中植入了专用于触摸的API的流行平台。有些其它的浏览器制造方创建了类似的API接口,植入浏览器并与iOS的实现兼容,现在被描述为"触摸事件版本1"规范。触摸事件在桌面环境被Chrome和Firefox支持,在iOS环境被Safari支持,在Android环境被Android浏览器支持,还有其它的移动浏览器比如Blackberry浏览器。

我的同事Boris Smus写了一篇关于触摸事件的HTML5Rocks教程,如果你以前没有看过触摸事件,这仍然是一个很好的开始。事实上,如果你以前没有处理过触摸事件,在你继续以前,现在就去阅读那篇文章吧。你去吧,我等着。

结束了?现在你对触摸事件有了一些基础知识,写一个可触摸的交互的挑战在于,触摸交互与鼠标(以及仿真鼠标的轨迹板和轨迹球)事件极为不同——虽然典型的触摸接口试图在模仿鼠标,但这种模仿并不完美或者说完整;你确实需要处理这两种交互模式,而且可能会不得不单独的支持每一个接口。

用户或许有触屏和一个鼠标

许多开发人员创建的网站会静态的检测环境是否支持触摸事件,之后再假设他们只需支持触摸(不需要支持鼠标)事件。现在这是一个错误的假设——相反的,仅仅因为触摸事件的存在并不意味着用户主要使用的就是触摸输入设备。像Chromebook Pixel笔记本之类的设备以及一些Windows 8便携电脑现在已经可以同时支持鼠标与触摸式输入方法,在不久的未来还会有更多。在这些设备中,用户同时使用鼠标和触屏与应用交互是很自然的事情,所以“支持触屏”并不等于“不需要支持鼠标”。你不能将这个问题想成“我需要写两种不同的交互方式并且在它们之间切换”,你要想清楚这两种交互怎样独立的工作,也要想清楚怎样让它们协同工作。在我的Chromebook Pixel笔记本上,我经常使用轨迹板,但也会伸手触摸屏幕——在同一个应用或页面中,当时怎么感觉自然我就怎么做。从另一方面来说,有些触屏的便携式电脑用户几乎从不使用触屏——因此触摸输入的存在不应该禁止或者隐藏鼠标控制。

不幸的是, 很难知道用户的浏览器环境是否支持触摸输入,理想情况下,台式机上的浏览器应该显示对触摸事件的支持,这样就可以随时安装触摸屏显示器(例如:通过KVM连接触摸屏是否可行). 基于所有这些原因,您的应用不应该尝试在触摸和鼠标之间切换--只需要两种方式都支持.

指针事件

在Windows8系统的IE10浏览器中. 微软引入了一种叫做指针事件的心的模型.指针事件是鼠标事件和触摸输入事件,还有其他输入方式比如笔输入的联合. 将指针事件模型提交到W3C标准还有很多工作要做,在短时间内, 已经有像PointerEvents 和 Hand.js 这样的类库供你在代码中实现指针事件,从而避免为鼠标和触摸分别提供支持. 为了更好的触摸和鼠标交互,你可能需要为鼠标和触摸事件分别自定义用户体验,但在很多情况下,统一事件处理都简化了这样的过程. 然而,这种模型还面临着巨大的挑战,它需要支持冗余的输入模型,也还没有被广泛支持,而且还需要很多事件来将它变成一个稳定的,跨浏览器的标准.

与此同时,最好的建议是同时支持鼠标和触摸交互模型.同时支持触摸和鼠标事件还面临着很多挑战,所以这篇文章对这些挑战以及克服这些挑战的策略进行了分析。另外,有些建议只是一般的“实现触摸”的建议,所以,如果你已经熟悉了在移动环境中实现触摸,这可能是多余的.

同时支持鼠标和触屏

点击和轻拍-“自然的”事物的顺序

第一个问题是传统的触摸界面技术想要模仿鼠标的点击-很显然,在触屏技术应用到应用程序之前,是仅仅只能和鼠标事件进行交互的。你可以把这个作为一个快捷键使用-因为“点击”事件将会继续被淘汰,无论用户是用鼠标点击还是用手指轻敲屏幕。然而,这个快捷方式还有一些问题。

首先,你在设计先进的触屏交互技术的时候必须要很仔细。:当用户使用鼠标,它就会通过点击事件给出应答,但是当用户触摸屏幕的时候,触摸和点击事件都会发生。对于一个简单的点击事件,其顺序是:

  1. 触屏开始
  2. 触屏移动
  3. 触屏结束
  4. 鼠标悬停
  5. 鼠标移动
  6. 鼠标按下
  7. 鼠标弹起
  8. 点击

当然,这个也意味着如果你正在处理触屏事件,比如说触屏开始,你需要确定你没有在同时处理相应的鼠标按下以及/或是点击事件。如果你能取消这个触屏事件(在事件处理程序中访问perventDefault()方法),然后在这次触屏中没有鼠标事件出现。其中触摸处理程序最重要的一个规则是: 使用事件处理程序中的perventDefault()方法,所以默认的鼠标仿真处理就不会发生。.

然而,这样也限制了其他默认浏览器的的行为(像scrolling)-虽然通常你在你的处理程序中完全的处理触屏事件,并且你想要禁止默认的行为。一般来说,你要么去处理和取消所有触屏事件,要么避免有一个对应这个事件的处理程序。

其次,当用户在移动设备上触摸一个网页上的某个元素时,相对于鼠标事件(mousedown)处理,那些没有为移动设备交互做专门设计的网页处理touchstart事件会有一个至少300毫秒的延迟。若你身边有触控设备,可以试试这个 example,看看这个延迟效果。或者,也可以使用Chrome,打开Chrome开发者工具中打开 "Emulate touch events",可以帮助你在非触控系统上测试触控接口。

这个延迟是用来给浏览器判定用户是否在采用其他的手势操作,特别是双点缩放。很明显,这个延迟在需要对手指点击做出瞬时相应时会引起问题。已经有个正在进行中的工作尝试对这些会由于延迟而引起问题的场景做出限制。

鼠标移动事件不是通过触摸实现的

在这一点上,非常值得注意的是,通过触摸接口对于鼠标事件的仿真通常并不扩展到仿真鼠标移动事件,所以如果你创建了一个使用鼠标移动事件的鼠标驱动控制,它可能不会再可触摸设备上很好的工作,除非你也明确的为其添加触摸移动事件的处理程序.

浏览器在Html控制上通常会自动实现对于触摸交互的适当响应 - 所以,比如,Html5Range控制只会在你使用触摸交互的时候起作用. 然而,如果你已经实现了自己的控制,它们可能不会响应点击拖动类型的交互;实际上,一些通用的类库(比如jQueryUI)还没有像这样实现本地化的支持触摸交互(尽管jQueryUI提供了像“猴子补丁”一样的修补来解决这个问题). 这是我在升级我的Web Audio Playgroud应用来支持触摸事件时遇到的第一个问题 - 滑动条是基于jQueryUI的,所以它们不支持点击拖动交互. 我切换成 HTML5 Range 控制, 然后就可以了.当然,我简单地添加了触摸移动处理程序来升级滑动条,但是还有一个问题...

触摸移动和鼠标移动不是同一件事

我曾经见过一些开发者陷入的一个误区就是:让触摸移动和鼠标移动的处理器调用相同的代码. 这些事件的行为非常相近,但存在细微的不同- 特别是, 触摸事件总是以触摸发生时所在的元素为目标,然而鼠标事件则以当前位于鼠标指针下方的元素为目标. 这就是为什么我们有鼠标移上和鼠标移出事件,却没有响应的触摸移上和触摸移出事件,只有触摸事件.

对此最常见的刺痛你的方式就是如果你碰巧移除(或者重新定位)某个用户刚开始触摸的元素. 比如,假设一个图像切换模块中有一个触摸处理器来支持特定的滚动行为.随着现有图片的变更,你移除了一些元素,并添加了新的元素.如果用户碰巧开始触摸在其中的一张图片上,然后你又移除了它,你的处理器(作用于图片元素的祖先元素)将停止接收触摸事件(因为它们被分配到了一个当前DOM树中不存在的目标上)-那么看起来将是用户正把手指放在一个位置,尽管这个位置的元素已经移动了并且最后被移除了.

当然,你可以通过避免移除触摸事件开始时已经绑定触摸处理器的元素(或者祖先元素已经绑定处理器的元素). 或者,最好的方式是先不注册touchend或者touchmove处理器,一直等到获得了touchstart事件,然后为touchstart事件的目标元素添加touchmove/touchend/touchcancel处理器(然后在end/cancel事件发生时移除这些处理器).这样你就可以一直接收触摸事件,即使目标元素移动了或者被移除了.你可以在这里尝试-触摸红色的方框然后点击将它从DOM中移除.

触摸和 :Hover

鼠标指针把光标位置和动态选择区分开来,这使得开发者可以使用“移上”状态来隐藏和显示跟用户相关的信息.然而,很多触摸接口当前都不能检测到手指“悬浮”在某个目标上面- 所以,通过这种方式提供重要的语义信息(比如,提供“这个控制是什么?”弹出层)是不可行的, 除非你提供了一种触摸友好的方式来提供信息. 对于如何使用hovering来向用户展示信息你需要谨慎.

然而,足够有趣的是, CSS的:hover伪类在某些情况下可以通过触摸触发- 轻巧某个元素使其具有:hover状态时当手指按下的时候,它也获得了:hover状态.(在IE中,:hover只有当用户的手指按下的时候才起作用,其他浏览器中:hover被一直保持有效直到下一次敲打或者鼠标移动)这是一种在触摸接口中实现弹出菜单的有效方式-副作用就在于此时:hover状态也触发了.例如:

<style>
img ~ .content {
  display:none;
}

img:hover ~ .content {
  display:block;
}
</style>

<img src="/awesome.png">
<div class="content">This is an awesome picture of me</div>

一旦另一个元素被敲击,当前元素就不在处于活跃状态,hover状态也会消失,就像用户使用鼠标时将鼠标指针移到了元素外面一样. 你可能也希望把内容包裹在一个<a>元素中来实现制表位的效果,那样用户可以通过鼠标移上或者点击,触摸敲击或者键盘按下来显示或者隐藏额外信息,而不需要Javascript控制.当我开始让我的Web Audio Playground 能够使用触摸接口时,弹出菜单已经能够很好的响应触摸事件的时候我很高兴,因为我已经用过这种结构了!

上面的方式在基于鼠标指针的接口和触摸接口中都能够很好的工作.这与"title"是相比较而言的,它在元素被激活的时候将不会显示出来:

<img src="/awesome.png" title="this doesn’t show up in touch">

触摸精度 vs. 鼠标精度

鼠标跟现实中的老鼠在概念上是分离的,区别在于他们非常精确,因为底层操作系统通常会追踪指针的精确像素精度. 移动开发者另一方面已经知道手指在触摸屏上的触摸并不如此准确,主要是由于跟屏幕交互时手指表面积的尺寸太大(部分原因是由于你的手指挡住了屏幕).

很多个人和公司都对如何设计能够容纳基于手指交互的应用和网站进行了大量的用户研究,很多书也是关于这个话题的.基本的意见就是通过增加填补空间(padding)来增加目标对象的尺寸,然后通过增加元素之间的间距(margin)来降低错误敲击的可能性.(Margins不包含在处理触摸和点击事件的敲击检测中,padding却包含在这其中)对于 Web Audio Playground系统一个主要的修补工作就是增加连接点的尺寸,这样他们就能够更准确的被触摸.

很多浏览器厂商在处理基于触摸接口的时候也进入了逻辑来帮助在用户触摸屏幕时准确定位目标元素,同时降错误点击的可能性-尽管这样做通常修正的是点击事件,而不是移动事件(尽管IE好像也修改了mousedown/mousemove/mouseup事件).

有限的使用触控处理器, 否则滚屏会卡顿

把你的触控处理器限制在你需要他们的地方也很重要;触控元素可能非常消耗带宽,所以滚动屏幕的时候要尽量避免引发触控处理器(因为你的触控处理器可能会干扰到浏览器滚屏优化-现代的浏览器会试着用显卡线程来处理屏幕滚动,但如果他们每次都要检查javascript来知道是否有事件需要被app处理,那基本上这个优化就废了。). 你可以试试这里的浏览器行为的一个示例.

一条避免这个问题的技巧就是只在你的ui中很小一部分使用触控事件处理器, 把你的触控处理器放在这里(比如,不要放在整个页面的body标签里。); 简而言之,尽可能的限制你的触控处理器的使用范围.

多点触控

最后一件有趣而又具有挑战性的事是,尽管我们称之为“触控”用户界面,几乎所有的支持都是多点触控的,也就是说应用程序接口支持每次不止一次触控输入。当你准备在你的应用中支持触控时,你应该考虑多点触控会如何影响你的应用程序。

如果你开发了主要靠鼠标驱动的程序的话,那么你会习惯于用至多一个光标点的系统建立,它不会典型地支持多个光标。对于大多数应用,你将会仅仅把触控事件映射到一个单独的光标接口,但是,我们见过的大多数桌面触控输入软件可以处理至少2次同时的输入,而且大多数新的软件似乎支持至少5次同时的输入。比如说开发屏幕钢琴按键,你要能够支持同时的多点触控输入。 目前实现了的W3C触控API接口没有能决定软件能够支持多少触控点的API,因此你将不得不尽最大努力估计你的用户将需要多少触控点,或者注意观察现实中需要多少触控点并适应之。例如,在一个钢琴应用中,如果你从没见过需要两个以上的触控点,那么你可能会想增加一些“和弦”界面了。PointerEvents API就有一个能够决定“和弦”性能的接口。

触摸起来

我希望这篇文章给你提供了一些指导,有关于实现触摸与鼠标交互时遇到的普遍难题。当然,比任何其他建议都重要的是,你应该在手机,平板电脑,还有混合了鼠标和触摸的桌面环境下测试你的应用。如果你没有触摸与鼠标硬件,可以使用Chrome的"模拟触摸事件",以便测试不同的场景。

依据这些指导创建吸引人的互动体验,使其良好的工作于触摸输入,鼠标输入,还有甚至是同时这两种交互,这不但是可能的,也是相对较为容易的。

参考资料