在网页上实现 3D Touch 效果

2016 年 9 月 14 日凌晨,iOS 10 正式版开始向用户推送更新,而 iOS 10 搭载的 Safari10 亦带来了不少新特性,其中就有 3D Touch 事件的支持(官方文档What’s New in Safari 10)。

Force Touch 与 3D Touch

说到 3D Touch 不能不提与之相似的 Force Touch。Force Touch 是苹果公司在 2014 年 9 月公布的一项压力敏感屏幕技术,最早用于 Apple Watch,可识别轻点、轻按两种操作。随后 Force Touch 于 2015 年 9 月在 iPhone 6S 上得到改进并更名为 3D Touch,提供了更高灵敏度的触控力度识别、及更强的触感反馈,支持轻点、轻按及重按三主� APP 以及一些第三方 APP(如微信、Facebook、Twitter 等)也都很好的支持了 Peek and Pop 这种 3D Touch 交互形式。

网页中的 3D Touch

要在网页中实现 3D Touch,需要用到以下两个知识点:

  • touch.force
    在 touch 对象中包含有一个名为 force 的只读属性,它的取值从 0 到 1,表示的是触碰点的按压力度,0 表示没有检测到压力,而 1 则是设备能识别出的最大压力。
  • touchforcechange
    touchforcechange 是 Safari 10 新增的事件,该事件会在按压力度改变时被触发。
    (注:在 MacOS Safari 上也有与之对应的 webkitmouseforcechanged 事件,该事件会在支持 Force Touch 的 Trackpad 上反应出按压力度值 force 的变化,但本文仅讨论手机设备的情况)

实现 3D Touch 效果

要实现 3D Touch 效果,关键在于实时地获取 touch.force 的值。而由于网页上的 3D Touch 很大程度上受限于设备及浏览器的支持情况,因此我们划分以下 3 种情况,分别来看看要如何实现:

  • 支持 3D Touch 且升级到了 iOS 10 的设备
    在这种最为理想的情况下,只需要监听 touchforcechange 事件即可获取到 force 的当前值,将 force 值的变化以适当的形式反馈在界面上以实现 3D Touch 效果。
  • 支持 3D Touch 但系统版本低于 iOS 10 的设备
    这种情况虽然无法监听 touchforcechange 事件,但 Touch 对象的 force 属性仍然可以反应出正确的按压力度,可以巧妙地设置一个定时器,以轮询的方式获取 force 的当前值。
  • 不支持 3D Touch 的设备
    这种情况下 touch.force 的取值始终为 0,虽然可以用长按的交互形式来代替,但建议还是以优雅降级的方式,索性就不处理了吧。

一个 3D Touch 的例子

看到这里你肯定想说 “Shut up and show me the code…”。好的,那我们来看一个例子,在这个示例页面,用支持 3D Touch 的设备按压蓝色按钮可以将树懒兄逗笑哦,嘿嘿嘿 ~

你可以戳我进行预览注意请使用 iOS Safari 浏览器进行访问!使用 iOS Safari 浏览器!!使用 iOS Safari 浏览器!!!重要的事情要说三遍,因为目前微信 WebView 并不支持 3D Touch。

实现思路其实比较简单,根据刚刚说到的知识,我们分别监听 touchforcechangetouchstarttouchendtouchcancel 事件:

  • touchstart 事件中,启动一个定时器轮询地去获取 touch.force 的值;
  • touchforcechange 事件中获取当前 touch.force 的值,并清除 touchstart 事件中设置的定时器,因为支持 touchforcechange 事件的话就没必要轮询了;
  • touchendtouchcancel 事件中把 touch.force 重置为 0,并清除定时器。

而树懒兄大笑的动画则用的是以下这张雪碧图,根据当前 touch.force 值来设置 background-position 以显示对应的动画帧来实现的。

你可以访问这个 Github 项目 来查看源码,核心代码位于 ThreeDTouch.js,该文件封装了一个名为 ThreeDTouch 的类,事例化时传入一个 DOM对象即可在 callback 中获取到按压力度值的变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
/**
* 3D Touch 事件处理器,传入要监听的 DOM 对象,在 callback 回调中获取当前 force 值
*
* @param { HTMLElement } el - 要监听的 DOM 对象
* @param { Function } callback - 带有 force 值的回调函数
*
*/
function ThreeDTouch(el, callback) {
this.el = el
this.callback = callback
this._bindEvents()
}
ThreeDTouch.prototype = {
//绑定相关 touch 事件
_bindEvents: function() {
var events = ['touchforcechange', 'touchstart', 'touchend', 'touchcancel']
events.forEach(function(event) {
this.el.addEventListener(event, this, false)
}.bind(this))
},
//分派 touch 事件
handleEvent: function(ev) {
switch (ev.type) {
case 'touchforcechange':
this._touchForceDidChange(ev)
break
case 'touchstart':
this._touchDidStart(ev)
break
case 'touchend':
case 'touchcancel':
this._touchDidEnd(ev)
}
},
//force 值改变时
_touchForceDidChange: function(ev) {
var force = ev.touches[0].force
this.callback(force)
clearTimeout(this.timeoutId) //支持 touchforcechange 的话则取消轮询
},
_touchDidStart: function(ev) {
var touch = ev.touches[0]
this._checkForce(touch)
},
_touchDidEnd: function(ev) {
this.callback(0)
clearTimeout(this.timeoutId)
},
//轮询地获取 force 值
_checkForce: function(touch) {
this.callback(touch.force)
this.timeoutId = setTimeout(this._checkForce.bind(this, touch), 16) }
}

本文转载自:在网页上实现 3D Touch 效果 - 凹凸实验室