谷歌 Web 开发最佳实践手册(3.2):让你的站点支持触屏

【伯乐在线提示】:① 5月6日,谷歌开发者中心推出了一个 Web 开发最佳实践手册。伯乐在线资源频道摘编该资源后,已邀请一些关注 Web 开发的朋友参与翻译手册。② 由于译者朋友几乎都是已在职,都是在工作之余参与,每位的翻译进度会不一样(请理解),所以手册中文版不会按照英文版章节顺序发布。③ 手册中文版尚不完整,请不要转载,谢谢合作。


【导读】:从手机到桌面屏幕,越来越多的设备拥有了触摸屏。当用户使用你的界面时,你的应用应该直观且优雅地支持触摸操作。

 

3.2.1 让页面元素响应触摸状态

从手机到桌面屏幕,越来越多的设备拥有了触摸屏。当用户使用你的界面时,你的app应该直观且优雅的支持触摸操作。

 

添加触摸状态的样式

你有没有触摸或点击一个页面上的元素,有没有奇怪为什么这个站点的页面元素可以检测这些状态?

当用户触摸你的界面中的元素时,仅仅是简单地修改元素的颜色就可以让用户感知你的站点可以正常工作。不只是这个作用,还可以让用户感受到页面的即时响应。

使用伪类来切换不同触摸状态下的样式

最快地支持触摸的方式就是在页面元素的触摸状态切换时修改其样式。

学习重点:

  • 修改:hover:active:focus状态下的UI,让用户感觉你的站点是可以即时响应的。
  • 不要覆盖浏览器的默认touch和focus行为,除非你自己实现了对应的UI变化。
  • 对于用户可能进行触摸操作的页面元素,禁用其文字选择功能。除非用户有需要会拷贝或选择文字。

DOM元素可以处于以下四种状态:默认、focus、hover和active。如果你想要改变这些状态下的UI,我们需要使用这三种伪类::hover:focus:active。例如:

看下伪类所对应的触摸状态

覆盖浏览器默认的触摸状态样式

不同的浏览器实现了它们自己特有的触摸状态样式。当你想要实现自己的样式时,就需要同时覆盖掉浏览器的默认样式。

记住:

  • 只有当你实现了自己的样式时,才需要覆盖浏览器的默认样式。

 

覆盖Tap高亮样式

当移动设备刚出现的时候,很多站点都没有激活状态下的样式。结果很多浏览器在用户触摸浏览器的时候添加了高亮颜色或是其他样式。

Safari和Chrome添加了一个高亮颜色作为Tap高亮样式。这可以通过设置CSS样式-webkit-tap-highlight-color来修改默认样式。

Windows Phone上的Internet Explorer有一个类似的行为,但是它需要meta标签来重写:

重写FirefoxOS按钮的状态样式

Firefox的伪类-moz-focus-inner为每个可触摸元素默认添加了一个outline样式。你可以通过设置border:0来移除outline样式。

如果你使用了<button>元素,FirefoxOS会其默认添加个渐变的背景。你可以通过设置background-image:none来覆盖这样式。

重写Focus状态下的ouline样式

使用outline:0可以覆盖focused元素的outline颜色。

禁用可触摸UI的user-select功能

在一些移动浏览器上,用户长按屏幕就可以选择文字。但当用户不小心按一个按钮时间太长,并不会触发点击事件而会触发按钮文字的选择,这并不是好的用户体验。

记住:

  • 如果页面元素中的信息对用户有用,例如电话号码、email地址等等的东西,你不应该轻易地禁用user-select

引用

触摸状态的伪类。

例子 描述
:focus Button with Focus State 当你通过tab键切换焦点时,Focus状态可以提示用户哪个元素处于激活状态
:active Button in Pressed State 这个状态表示页面元素被选中,例如用户正在点击或触摸一个元素
:hover Button in Pressed State 当用户的光标处于页面元素之上时,该元素就会进入这个状态。在hover状态下的 UI 变化,有助鼓励用户与此元素交互。

 

3.2.2 实现自定义手势

如果你想要为自己的站点实现一个自定义的交互和手势,那么有两点要记住:要支持哪些移动浏览器和如何保持高帧率。在这篇文章中我们讲一探究竟。

使用事件来响应触摸操作

根据你想要实现的touch操作,你就需要在如下阵营中选其一:

  • 用户同时只与一个特别的元素交互
  • 用户与多个元素在同一时间交互

两者必选其一。

如果用户只需要和一个元素交互,那么只要手势操作开始,你可能就需要把所有的touch事件放在那个元素上。例如,在其他元素上滑动也可以控制要移动的元素。

然后,如果你期望用户与多个元素在同一时间交互,你应该将touch操作限制到特定的元素上。

学习重点:

  • 要支持所有的设备,那就需要处理touch、mouse和MS Pointer事件
  • 永远将开始整个交互的事件监听器绑定在元素本身上
  • 如果你想让用户与一个指定的元素交互,那么将移动和结束的监听器绑定在document上面;确保在结束的监听器中解绑移动和结束的监听器
  • 如果你想要支持多点触摸,要么在各个元素上绑定与其对应的移动和结束touch事件,要么在一个元素上处理所有的touch事件

添加事件监听器

大多数移动浏览器都实现了Touch事件和鼠标事件。

你需要绑定的事件名是:touchstarttouchmovetouchendtouchcancel

在某些情况下,你可能也需要支持鼠标的交互;那么你可以使用这些事件:mousedownmousemovemouseup

对与Windows Phone的设备,你需要支持一系列Pointer Events。Pointer Events是鼠标和touch事件的合集。目前这只在IE 10+上支持,事件名分别是MSPointerDownMSPointerMoveMSPointerUp

Touch、鼠标和Pointer Events是在你的应用中增加手势操作的重要基础。(查看下Touch、鼠标和Pointer Events

使用addEventListener()方法可以注册这些事件,同时还要传递回调函数和一个布尔值。这个布尔值决定了是否使用捕获模式。为true时表示使用捕获模式时,你可以在其他元素之前捕获或打断事件。

这段代码一开始先检查window.navigator.msPointerEnabled来判断是否支持Pointer Events。如果不支持就在添加touch和鼠标事件的监控。

处理单个元素的交互

你可能已经注意到,在上面的代码片段中只是添加了开始手势的事件。这是故意这么写的。

一旦手势操作在元素上开始,就添加移动和结束的事件监听器。这样浏览器可以通过touch事件监听器来检查touch操作是否发生了。并且处理地更快,因为在平时(手势操作开始前)不需要运行额外的javascript。

实现的步骤如下:

  1. 添加开始事件监听器到指定元素上;
  2. 在开始事件的监听器中,绑定移动和结束事件的监听器到document上。之所以要绑定在document上,是因为我们需要监控所有的事件,不仅仅是那个指定的元素;
  3. 处理移动事件;
  4. 在结束事件的监听器中,移除移动和结束事件的监听器;

如下是handleGestureStart方法的代码片段,它添加了移动和结束的事件监听器到document上:

我们使用的结束事件的回调函数是handleGestureEnd。在手势操作结束后它移除了移动和结束事件的监听器。

鼠标事件也使用相同的处理方法,因为用户的鼠标很可能会不小心移动到指定元素的外面。如果只是将移动事件绑定在元素上,那么很容易会不触发事件。相反地如果绑定在document上面,移动事件将继续触发不论鼠标在页面的哪个地方。

你可以使用Chrome DevTool中的“Show potential scroll bottlenecks”功能来了解touch事件的实现:

Enable Scroll Bottleneck in DevTools

处理多元素的交互

如果你期望用户在同一时间与多个页面元素交互,你可以将对应的移动和结束事件直接绑定到那些元素上。但是这只适用于touch事件。对于鼠标事件,你依旧需要将mousemovemouseup事件绑定到document上面。

如果我们只想要监控特定元素上的touch操作,那么我们可以把touch和pinter事件的移动和结束监听器直接绑定在元素上:

handleGestureStarthandleGestureEnd函数中,添加和移除鼠标事件的监听器到document上。

Touch操作时保持60fps

现在我们已经处理好开始和结束事件,我们就可以真正地实现touch事件了。

获取和存储Touch事件的坐标

对于任何开始和移动事件,你可以轻松地提取出xy坐标。

如下代码片段中通过targetTouches来判断是否是一个touch事件对象。如果事件对象是鼠标或者pointer事件,那么直接获取事件对象的clientXclientY值。

每个touch事件都有三种TouchList属性(见touch列表属性):

  • touches:包含了所有当前屏幕上Touch对象,无论它们起始于哪个元素上
  • targetTouches:包含了所有起始于当前事件所绑定的元素的touch对象
  • changedTouches:包含了所有因为事件触发而状态发生改变的Touch对象

大多数情况下targetTouches属性就足够了。

Request Animation Frame

因为事件回调函数在主线程中触发,我们就需要让回调的运行时间尽量短,以保持高帧率,并且避免卡顿。

在事件回调中使用requestAnimationFrame来修改UI。它可以让你可以在浏览器绘制一帧时更新UI,也可以帮你把一些操作放在回调函数外面。

一个典型的实现在开始和移动事件中把xy坐标保存下来。然后在移动事件的回调函数中调用requestAnimationFrame

在我们的DEMO中,我们在handleGestureStart中存储touch的初始化位置:

handleGestureMove方法中,如果需要则会在requestAnimationFrame之前存储y位置,然后将onAnimFrame函数传递作为回调函数:

onAnimFrame函数中,我们修改UI来移动元素。一开始我们先检查手势是否还在进行,来决定是否继续执行动画。如果需要执行动画,你们我们先计算出新的transform值。

一旦我们设置好transform,我们就将isAnimating变量设置为false,这样下一次touch事件中可以执行新的requestAnimationFrame

使用touch-action来控制滚动

CSS属性touch-action允许你在触摸时控制滚动行为。在我们的例子中,使用touch-action: none来禁用触摸滚屏功能。

如下是touch-action的所有可能值。

属性 描述
touch-action: auto 滚动正常工作,只要浏览支持触摸操作依旧可以触发水平或是垂直的滚动。
touch-action: none 触摸操作不能触发滚动
touch-action: pan-x 触摸操作可以触发水平滚动;但垂直滚动被禁止;
touch-action: pan-y 触摸操作可以触发垂直滚动;但水平滚动被禁止;

记住:

  • 使用touch-action: pan-xtouch-action: pan-y更好,因为你的目的明确,用户只能在元素上水平或垂直的滚动。

 

引用

touch事件的标准定义可以通过w3 Touch Event来获取。

Touch事件、鼠标事件和MS Pointer事件

这些事件是在你的应用中增加手势操作的重要基础。

  • 事件名:touchstartmousedownMSPointerDown
    • 描述:当手指第一次触摸一个元素或者鼠标按下时触发。
  • 事件名:touchmovemousemoveMSPointerMove
    • 描述:当手指在屏幕上移动或者用鼠标拖动时触发。
  • 事件名:touchendmouseupMSPointerUp
    • 描述:当手指离开屏幕或者鼠标松开时触发。
  • 事件名:touchcancel
    • 描述:当浏览器取消手势操作是触发此事件。

 

Touch list对象

每个touch事件对象都包含三种touch list属性:

  • 属性:touches
    • 描述:包含了所有当前屏幕上Touch对象,无论它们起始于哪个元素上
  • 属性:targetTouches
    • 描述:包含了所有起始于当前事件所绑定的元素的touch对象。例如,如果你的事件绑定在一个<button>上,那么这个属性就只包含起始于这个按钮的touch对象。如果你的事件绑定在document上,那么这个属性就包含这个document上的所有touch对象。
  • 属性:changedTouches
    • 描述:包含了所有因为事件触发而状态发生改变的Touch对象
      1. 对于touchstart事件,这个属性包含了在当前事件中刚刚激活的touch对象
      2. 对于touchmove事件,这个属性包含了自从上一次move事件触发后位置移动的touch对象
      3. 对于touchendtouchcancel,这个属性包含了刚刚离开屏幕的touch对象
收藏 1 评论

关于作者:周敏明

周敏明:前端开发工程师,专注于Web前端开发!(新浪微博:@敏明君,个人博客:mzhou,nodejs.in) 个人主页 · 我的文章

相关文章

可能感兴趣的话题



直接登录
最新评论
跳到底部
返回顶部