0%

在网页上实现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,提供了更高灵敏度的触控力度识别、及更强的触感反馈,支持轻点、轻按及重按三个维度。

支持3D Touch的设备,目前有iPhone 6S、iPhone 6S Plus、iPhone 7以及iPhone 7 Plus(注:截止2018年,还要加上iPhone 8和iPhone X等)。这是后文的硬件要求。

感受3D Touch

3D Touch最为典型的交互有Quick Actions和Peek and Pop两种。在APP图标上重按呼出一组快捷操作菜单,这即是典型的Quick Actions:

而使用Peek and Pop则可以快速地对内容进行预览,以及后续的其他操作:

如上图所示,在系统的邮件APP中,以一定的力度按压邮件列表中的某一项,会触发Peek弹出一个内容预览窗口(在Peek状态下上滑还能触发Quick Actions调出一些快捷操作项哦),如果继续加大按压力度,则会触发Pop进入邮件内容界面,这整个过程就称之为Peek and Pop。

除了邮件APP以外,信息、照片等多个系统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。

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

  • 在touchstart事件中,启动一个定时器轮询地去获取touch.force的值;
  • 在touchforcechange事件中获取当前touch.force的值,并清除touchstart事件中设置的定时器,因为支持touchforcechange事件的话就没必要轮询了;
  • 在touchend及touchcancel事件中把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
54
/**
* 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 效果 - 凹凸实验室

🍭支持一根棒棒糖!