触摸和鼠标:第一次再次在一起
近三十年来,桌面计算体验一直围绕着键盘和鼠标或触控板作为我们的主要用户输入设备。 然而,在过去十年中,智能手机和平板电脑带来了一种新的交互模式:触摸。 随着支持触控的 Windows 8 机器的推出,以及现在令人敬畏的支持触控的 Chromebook Pixel 的发布,触控现在正成为预期桌面体验的一部分。 最大的挑战之一是构建不仅适用于触摸设备和鼠标设备的体验,而且适用于用户将同时使用两种输入法的设备上——有时同时!
本文将帮助您了解触摸功能是如何内置到浏览器中的,如何将这种新的界面机制集成到现有应用程序中,以及触摸如何与鼠标输入配合得很好。
Web 平台中的触控状态
iPhone 是第一个在网络浏览器中内置专用触摸 API 的流行平台。 其他几个浏览器供应商已经创建了类似的 API 接口,旨在与 iOS 实现兼容,现在由 “触摸事件版本 1”规范描述 。 桌面上的 Chrome 和 Firefox 以及 iOS 上的 Safari 和 Chrome 以及 Android 上的 Android 浏览器以及其他移动浏览器(如黑莓浏览器)都支持触摸事件。
我的同事 Boris Smus 写了一篇很棒的 关于 Touch 事件的 HTML5Rocks 教程, 如果您以前没有看过 Touch 事件,这仍然是一个很好的入门方法。 事实上,如果您以前没有使用过触摸事件,请在继续之前阅读那篇文章。 继续,我等着。
全做完了? 现在您已经对触摸事件有了基本的了解,编写启用触摸的交互的挑战在于触摸交互可能与鼠标(以及模拟鼠标的触控板和轨迹球)事件有很大不同——尽管触摸界面通常会尝试模拟老鼠,这种模拟并不完美或不完整; 您确实需要同时处理这两种交互方式,并且可能必须独立支持每个界面。
最重要的是:用户可能有触摸和鼠标。
许多开发人员已经构建了静态检测环境是否支持触摸事件的站点,然后假设他们只需要支持触摸(而不是鼠标)事件。 现在这是一个错误的假设——相反,仅仅因为存在触摸事件并不意味着用户主要使用该触摸输入设备。 Chromebook Pixel 和一些 Windows 8 笔记本电脑等设备现在支持鼠标和触摸输入法,在不久的将来还会支持更多。 在这些设备上,用户很自然地同时使用鼠标和触摸屏与应用程序交互,因此“支持触摸”与“不需要鼠标支持”是不一样的。 您不能将问题视为“我必须编写两种不同的交互风格并在它们之间切换”,您需要考虑两种交互如何协同工作以及独立工作。 在我的 Chromebook Pixel 上,我经常使用触控板,但我也会伸手触摸屏幕——在同一个应用程序或页面上,我会做目前最自然的事情。 另一方面,一些触摸屏笔记本电脑用户根本不会使用触摸屏——因此触摸输入的存在不应禁用或阻碍鼠标控制。
不幸的是,很难知道用户的浏览器环境是否支持触摸输入。 理想情况下,台式机上的浏览器将始终指示对触摸事件的支持,因此可以随时连接触摸屏显示器(例如,如果通过 KVM 可用)。 由于所有这些原因,您的应用程序不应尝试在触摸和鼠标之间切换 - 只需同时支持两者!
指针事件
在 Windows 8 上的 IE10 中,微软引入了一种称为指针事件的新模型。 指针事件是鼠标事件和触摸输入以及笔输入等其他输入方式的统一。 有 标准化指针事件模型的工作 ,并且在短期内,有像 PointerEvents 和 Hand.js 这样的库,您可以使用它们来原型化指针事件如何在您的代码中工作以消除一些需要独立支持鼠标和触控。 对于真正出色的触摸和鼠标交互,您可能需要分别自定义鼠标和触摸的用户体验,但统一的事件处理可以在许多情况下使这更容易。 然而,这种模型存在重大挑战——即,它需要支持冗余输入模型,而且还没有得到广泛支持——并且需要一些时间才能稳定下来,形成一个可靠的跨浏览器标准。
同时,最好的建议是同时支持鼠标和触摸交互模型。 同时支持触摸和鼠标事件有很多挑战,因此本文解释了这些挑战以及克服它们的策略。 此外,其中一些建议只是一般的“实现触摸”建议,因此如果您已经习惯于在移动环境中实现触摸,那么它可能是多余的。
同时支持鼠标和触控
#1 – 点击和轻敲 – 事物的“自然”秩序
第一个问题是触摸界面通常会尝试模拟鼠标点击——显然,因为触摸界面需要在以前只与鼠标事件交互过的应用程序上工作! 您可以将其用作快捷方式——因为“单击”事件将继续被触发,无论用户是用鼠标单击还是在屏幕上轻敲手指。 但是,此快捷方式存在一些问题。
首先,在设计更高级的触摸交互时必须小心:当用户使用鼠标时,它会通过点击事件进行响应,但是当用户触摸屏幕时,触摸和点击事件都会发生。 单击一下,事件的顺序是:
- touchstart
- touchmove
- touchend
- mouseover
- mousemove
- mousedown
- mouseup
- click
当然,这意味着如果您正在处理 touchstart 之类的触摸事件,则需要确保您不处理相应的 mousedown 和/或 click 事件。 如果您可以取消触摸事件(在事件处理程序中调用 preventDefault()),则不会为触摸生成鼠标事件。 触摸处理程序最重要的规则之一是:
在触摸事件处理程序中使用 preventDefault(),因此不会发生默认的鼠标模拟处理。
但是,这也阻止了其他默认浏览器行为(如滚动)——尽管通常您完全在处理程序中处理触摸事件,并且您会想要禁用默认操作。 通常,您要么想要处理和取消所有触摸事件,要么避免为该事件设置处理程序。
其次,当用户在移动设备上点击网页中的某个元素时,并非为移动交互而设计的页面在 touchstart 事件和鼠标事件处理 (mousedown) 之间会有至少 300 毫秒的延迟。 如果您有触摸设备,可以查看此 示例 - 或者,使用 Chrome,您可以在 Chrome 开发人员工具中打开 “模拟触摸事件” ,以帮助您在非触摸系统上测试触摸界面!
这种延迟是为了让浏览器有时间确定用户是否正在执行另一个手势——特别是双击缩放。 显然,如果您希望对手指触摸做出即时响应,这可能会出现问题。 正在进行 的工作 是试图限制这种延迟自动发生的情况。
Chrome for Android | Android Browser | Opera Mobile for Android | Firefox for Android | Safari iOS | |
不可缩放的视口 | No delay | 300ms | 300ms | No delay | 300ms |
无视口 | 300ms | 300ms | 300ms | 300ms | 300ms |
避免这种延迟的第一个也是最简单的方法是“告诉”移动浏览器您的页面不需要缩放 - 这可以使用固定视口来完成,例如通过插入到您的页面中:
<meta name="viewport" content="width=device-width,user-scalable=no">
当然,这并不总是合适的 - 这会禁用捏缩放,这可能是出于可访问性原因而需要的,所以如果有的话,请谨慎使用(如果您确实禁用了用户缩放,您可能需要提供一些其他方式来增加文本应用程序中的可读性)。 此外,对于支持触摸的桌面设备上的 Chrome 和移动平台上的其他浏览器,当页面具有不可扩展的视口时, 此延迟不适用。
#2:鼠标移动事件不会被触摸触发
在这一点上需要注意的是,触摸界面中鼠标事件的模拟通常不会扩展到模拟 mousemove 事件 - 因此,如果您构建一个使用 mousemove 事件的漂亮的鼠标驱动控件,它可能不适用于触摸设备,除非您也专门添加了 touchmove 处理程序。
浏览器通常会自动为 HTML 控件上的触摸交互实现适当的交互 - 例如,HTML5 Range 控件只会在您使用触摸交互时工作。 但是,如果您实现了自己的控件,它们可能不适用于单击和拖动类型的交互; 事实上,一些常用的库(如 jQueryUI)还没有原生支持这种方式的触摸交互(尽管对于 jQueryUI,有几个猴子补丁修复了这个问题)。 这是我在升级我的 Web Audio Playground 应用程序以使用触摸时遇到的第一个问题 - 滑块是基于 jQueryUI 的,因此它们不适用于单击和拖动交互。 我改用 HTML5 Range 控件,它们工作正常。 当然,或者,我也可以简单地添加 touchmove 处理程序来更新滑块,但是这样做有一个问题……
#3:Touchmove 和 MouseMove 不是一回事
我见过一些开发人员陷入的一个陷阱是让 touchmove 和 mousemove 处理程序调用相同的代码路径。 这些事件的行为非常接近,但略有不同——特别是, 触摸事件总是以触摸开始的元素为目标,而鼠标事件则以当前鼠标光标下的元素为目标。 这就是为什么我们有 mouseover 和 mouseout 事件,但没有相应的 touchover 和 touchout 事件——只有 touchend。
如果您碰巧删除(或重新定位)用户开始触摸的元素,这可能会咬到您。 例如,想象一个图像轮播,整个轮播上有一个触摸处理程序,以支持自定义滚动行为。 随着可用图像的变化,您会删除一些 <img /> 元素并添加其他元素。 如果用户碰巧开始触摸其中一张图像,然后您将其删除,您的处理程序(位于 img 元素的祖先上)将停止接收触摸事件(因为它们被分派到不再存在的目标在树中)——看起来用户将手指放在一个地方,即使他们可能已经移动并最终移除了它。
您当然可以通过避免在触摸处于活动状态时删除具有(或具有)触摸处理程序的祖先的元素来避免此问题。 或者,最好的指导不是注册静态的 touchend/touchmove 处理程序,而是等到您收到 touchstart 事件,然后将 touchmove/touchend/touchcancel 处理程序添加到 touchstart 事件的 目标 (并在结束/取消时删除它们)。 这样,即使目标元素被移动/移除,您也将继续接收触摸事件。 玩 在这里 - 触摸红色框并按住 hit escape 将其从 DOM 中删除。
#4:触摸和:悬停
鼠标指针隐喻将光标位置与主动选择分开,这允许开发人员使用悬停状态来隐藏和显示可能与用户相关的信息。 然而,目前大多数触摸界面都不会检测到手指“悬停”在目标上——因此基于悬停提供语义上重要的信息(例如提供“这是什么控件?”弹出窗口)是不可以的,除非你也给出一个触摸友好的方式来访问此信息。 您需要注意如何使用悬停将信息传递给用户。
有趣的是,在某些情况下,CSS :hover 伪类可以由触摸界面触发——点击一个元素使其在手指向下时变为 :active,并且它还获得 :hover 状态。 (在 Internet Explorer 中,: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>
一旦点击了另一个元素,该元素就不再处于活动状态,并且悬停状态消失,就像用户使用鼠标指针将其从元素上移开一样。 您可能希望将内容包装在 <a> 元素中,以使其也成为制表位 - 这样用户可以在鼠标悬停或单击、触摸点击或按键时切换额外信息,而无需 JavaScript必需的。 我感到很惊喜 Web Audio Playground 与我的弹出菜单在触摸时已经很好地工作的触摸界面一起工作时,
上述方法适用于基于鼠标指针的界面以及触摸界面。 这与在悬停时使用“title”属性形成对比,当元素被激活时不会显示:
<img src="/awesome.png" title="this doesn’t show up in touch">
您可能希望考虑的其他类似悬停的语义:
- 将“触摸并按住”实现为“二次点击”。 在许多设备上——移动设备和桌面设备——已经使用长按来实现上下文菜单,所以你不应该使用自己的计时器——监听 oncontextmenu 事件,并确保取消默认行为。
- 使 UI 采用两个单点触摸事件来完成单击——第一次单击将显示悬停信息,第二次将完成操作。
- 如果您正在实现悬停效果作为提供帮助信息的一种方式——“此控件做什么”类型的信息——您可能希望只提供一个切换此行为的“帮助模式”。
#5:触摸与鼠标精度
虽然鼠标在概念上与现实脱节,但事实证明它们非常准确,因为底层操作系统通常会跟踪光标的精确像素精度。 另一方面,移动开发人员已经了解到,手指在触摸屏上的触摸并不那么准确,这主要是因为手指接触屏幕时的表面积大小(部分是因为您的手指挡住了屏幕)。
许多个人和公司已经就如何设计适应基于手指的交互的应用程序和站点进行了广泛的用户研究,并且已经编写了许多关于该主题的书籍。 基本建议是通过增加填充来增加触摸目标的大小,并通过增加元素之间的边距来减少不正确点击的可能性。 (边距不包括在触摸和单击事件的命中检测处理中,而填充是。)我必须对 Web Audio Playground 进行的主要修复之一是增加连接点的大小,以便更容易触摸它们准确。
许多处理基于触摸的界面的浏览器供应商也在浏览器中引入了逻辑,以帮助在用户触摸屏幕时定位正确的元素并减少错误点击的可能性——尽管这通常只纠正点击事件,而不是移动(尽管 Internet Explorer似乎也修改了 mousedown/mousemove/mouseup 事件)。
#6:保持触摸处理程序被包含,否则它们会破坏你的滚动
将触摸处理程序仅限于您需要它们的元素也很重要; 触摸元素的带宽可能非常高,因此避免在滚动元素上使用触摸处理程序很重要(因为您的处理可能会干扰浏览器对快速无卡顿触摸滚动的优化——现代浏览器尝试在 GPU 线程上滚动,但这是不可能的如果他们必须先使用 javascript 检查每个触摸事件是否将由应用程序处理)。 您可以查看 的示例 。
避免此问题的一条指导原则是确保如果您仅在 UI 的一小部分中处理触摸事件,则仅在其中附加触摸处理程序(而不是,例如,在页面的 <body> 上) ; 简而言之,尽可能限制触摸处理程序的范围。
#7:多点触控
最后一个有趣的挑战是,尽管我们一直将其称为“触控”用户界面,但几乎普遍支持实际上是多点触控——也就是说,API 一次提供多个触控输入。 当您开始在应用程序中支持触摸时,您应该考虑多次触摸可能会如何影响您的应用程序。
如果您一直在构建主要由鼠标驱动的应用程序,那么您习惯于使用最多一个光标点进行构建——系统通常不支持多个鼠标光标。 对于许多应用程序,您只是将触摸事件映射到单个光标界面,但我们看到的桌面触摸输入的大多数硬件都可以处理至少 2 个同时输入,并且大多数新硬件似乎支持至少 5 个同时输入,要开发 屏幕钢琴键盘 ,您会希望能够支持多个同时触摸输入。
当前实现的 W3C Touch API 没有 API 来确定硬件支持多少触摸点,因此您必须使用您的最佳估计来确定用户想要多少触摸点 - 或者,当然,注意多少触摸您在实践中看到并适应的要点。 例如,在钢琴应用程序中,如果您从未看到超过两个触摸点,您可能需要添加一些“和弦”UI。 PointerEvents API 确实有一个 API 来确定设备的功能。
润色
希望本文为您在实现鼠标交互的同时实现触摸的常见挑战提供了一些指导。 当然,比任何其他建议更重要的是,您需要在移动设备、平板电脑以及组合鼠标和触摸桌面环境中测试您的应用程序。 如果您没有触摸+鼠标硬件,请使用 Chrome 的“ 模拟触摸事件 ”来帮助您测试不同的场景。
遵循这些指导,不仅可以,而且相对容易构建引人入胜的交互体验,这些体验可以很好地与触摸输入、鼠标输入,甚至同时使用两种交互方式。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论