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 值的回调函数
*
*/
class ThreeDTouch {
constructor(el, callback) {
this.el = el;
this.callback = callback;
this._bindEvents();
};
//绑定相关 touch 事件
_bindEvents() {
var events = ['touchforcechange', 'touchstart', 'touchend', 'touchcancel'];
events.forEach(function(event) {
this.el.addEventListener(event, this, false);
}.bind(this));
};
//分派 touch 事件
handleEvent(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(ev) {
var force = ev.touches[0].force;
this.callback(force);
clearTimeout(this.timeoutId); //支持 touchforcechange 的话则取消轮询
};
_touchDidStart(ev) {
var touch = ev.touches[0];
this._checkForce(touch);
};
_touchDidEnd(ev) {
this.callback(0);
clearTimeout(this.timeoutId);
};
//轮询地获取 force 值
_checkForce(touch) {
this.callback(touch.force);
this.timeoutId = setTimeout(this._checkForce.bind(this, touch), 16);
};
}

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

🍭支持一根棒棒糖!
张书樵 微信支付

微信支付

张书樵 支付宝

支付宝