如何把手机触摸屏变成触控板(Touchpad)

鼠标使用简单直观,掌握起来也容易,但是通过手势触控操作的触控板却受到越来越多的欢迎。鼠标移动起来需要比较大的平面空间,还需要鼠标垫,使用限制大,时间长了,手腕会累。触控板克服了这些缺陷,掌握常用的操作手势后,用起来非常方便。我的台式电脑有时会接投影仪投屏,通过鼠标操作不太方便。我就想通过一个有趣的项目,把手机触摸屏改造为遥控器来控制投影仪。

想要实现手机触控远程操作电脑鼠标,需要解决3个最重要的问题:

1. 识别手机触控手势

浏览器已有手势操作相关标准,基于 Web 天然跨平台的优势,采用浏览器来识别手势也是很自然的事情,而且有现成的开源库 hammer.js 可以直接使用。hammer.js 可以识别各种手势以及手指与屏幕的接触点坐标。

查看 hammer.js 官方 Doc,主要支持以下手势:

  • Pan - 移动/拖动

通过 Hammer.Pan(options) 可以构建一个移动手势对象,后面需要把 Hammer 实例对象关联到 Dom 的某个 element 元素上面,这个 element 即支持相应的手势操作了。需要支持哪些手势,就把相应构建好的手势对象实例添加到 Hanmmer 实例对象中即可,如 Hammer.Pan。Pan 对象接受一个 options 参数对象,主要选项如下表:

选项 默认值 说明
event pan 事件名称,Pan 实例会触发的事件,所支持的事件列表后面给出
pointers 1 与屏幕接触点数量,如果移动鼠标指针,只需为 1,如果滑动滚动条,通常需要为 2
threshold 10 识别该手势所需最小的移动距离(单位: 像素)
direction DIRECTION_ALL 移动方向,包含上、下、左、右四个方向,滑动滚动条时很有用

pan: 代表下面所有的事件都需要触发

panstart: 移动开始事件

panmove: 移动中事件

panend: 移动结束事件

pancancel: 移动取消事件

panleft: 向左移动事件

panright: 向右移动事件

panup: 向上移动事件

pandown: 向下移动事件

其中 panleft/panright 手势配合 2 个接触点可以控制滚动条左右滑动。同理,panup/pandown 手势配合 2 个接触点可以控制滚动条上下滑动。进而替代鼠标滚轮。

panstart/panmove/panend 配合 1 个接触点可以控制鼠标指针移动,如果再配合后面会提到的 press,则可以实现控制鼠标拖动。panmove 事件会实时上报接触点的绝对坐标或者相对位移,以此可以实时计算鼠标指针应该移动的方向和距离。

  • Pinch - 缩放

可以通过 2 个或更多的接触点,实现手势控制缩放,本例暂时没有用到

  • Press - 按压

可以识别 1 个或多个接触点的按压和释放手势,和 Tap 点击手势有所区别。上面提到了,press 配合 panstart/panmove/panend 可以实现鼠标拖动

选项 默认值 说明
event press 事件名称,Hammer.Press 实例会触发
pointers 1 与屏幕接触点数量,如拖动鼠标(按住左键移动鼠标),需为 1
threshold 9 按压时接触点允许移动的最大距离(单位: 像素)
time 251 识别该手势要求保持按压状态的最短时间(单位: 毫秒)

press: 按压事件 pressup: 释放事件

  • Rotate - 旋转

可以通过 2 个或更多的接触点,实现手势控制旋转,本例暂时没有用到

  • Swipe - 快速滑动

可以通过 1 个或多个接触点的快速移动,实现手势控制翻页、切换 Tab 标签页等,本例暂时没有用到

  • Tap - 点击

可以通过 1 个接触点,实现鼠标左键单击、双击、甚至三连击等,也可以通过 2 个接触点点击模拟鼠标右键点击。

选项 默认值 说明
event tap 事件名称
pointers 1 1 个接触点点击代表鼠标左键单击,2 个接触点点击代表鼠标右键单击
taps 1 识别该手势的点击次数,如果单击为 1,双击为 2
interval 300 多次点击被识别为同一个点击事件的最大时间间隔 (单位: 毫秒)
time 250 点击时按压状态保持的最大时长 (单位: 毫秒)
threshold 2 点击时所允许的接触点移动距离 (单位: 像素)
posThreshold 10 多次单击位置的最大差距距离(单位: 像素)

tap: 点击事件

2. 手机与电脑之间低时延实时通信

手机和电脑之间可以通过 WIFI 建立 TCP 连接进行通信,要求手机和电脑连接相同的 WIFI,这样彼此之间可以进行通信。可以直接通过 socket.io 开源库实现,电脑端通过 Node.js 实现服务端,手机端浏览器作为客户端。因为是内网,连接质量有所保证,注意在初始化 socket.io 服务端和客户端时,可以通过配置选项控制直接建立 websocket 连接。而且 socket.io 对断线重连有非常好的支持,意外断开连接,或者重启服务端程序,甚至短时间内重启电脑,都可以自动重新连接。

虽然电脑和手机之间的数据传输量不大,但是实时性要求非常高,尤其在鼠标移动时,需要不停的计算鼠标指针的位置并不断的移动鼠标。如果数据包延时较大,鼠标的移动控制就有顿错感,体验很差。所以实际使用手机控制电脑时,要注意手机和电脑的 WIFI 信号质量、周围是否有蓝牙或者其他相同频道的干扰信号。这些干扰信号会使得通信速度下降,造成时延增加,使得鼠标出现滞后的顿错感。同理,如果路由器设备比较老了,还在用 2.4G 频率通信,如果在观看在线视频甚至游戏场景,电脑和路由器之间的数据包很多,也可能造成手机发出的触控数据包的实时性。

最理想的情况是,家里的路由器支持 5G 信号,并且信号覆盖比较理想,电脑和手机 WIFI 信号也不错,也没有其他无线干扰信号。2.4G 频段干扰信号比较多,如蓝牙、或者无线鼠标键盘等都在这个频段,很容易和 2.4G WIFI 相互之间产生信号干扰。

当然如果给台式机单独配置一个 USB 无线网卡,单独给手机来连接,进行触控数据包的实时传输,效果应该会更好,因为数据包不在经过路由器转发,手机和电脑距离又近,体验应该非常接近于无线蓝牙鼠标了。

3. 手势数据转换为相应的鼠标操作

现在手机产生的手势数据已经实时传输到了电脑端,现在需要调用鼠标驱动程序,对鼠标进行相应的控制。幸运的是,Node.js 端有个 robotjs 开源库,就像名字所暗示的,可以通过 js 实现 robot 控制电脑,让电脑可以自主操作,实现桌面自动化。可以实现一些桌面自动化测试任务,甚至实现游戏外挂。

通过 robotjs 可以控制鼠标、键盘、显示器等。下面列出了 Mouse 的主要 API:

setMouseDelay(ms): 设置鼠标时间延时(默认 10ms)

Argument Description Default
ms 延时 (ms) None

moveMouse(x, y): 移动鼠标到 (x, y)

Argument Description Default
x x 坐标 None
y y 坐标 None

moveMouseSmooth(x, y): 模拟人类操作,移动更平滑

Argument Description Default
x x 坐标 None
y y 坐标 None

mouseClick([button], [double]): 点击鼠标

Argument Description Default
button 接受左键 'left',右键 'right', 中间滚轮键 'middle' 'left'
double 是否双击 None

mouseToggle([down], [button]): 按压鼠标

Argument Description Default
down 按下 'down',释放 'up' 'down'
button 接受左键 'left',右键 'right', 中间滚轮键 'middle' 'left'

dragMouse(x, y): 拖动鼠标到 (x, y)

Argument Description Default
x x 坐标 None
y y 坐标 None

getMousePos(): 获取当前鼠标位置

scrollMouse(x, y): 滑动滚动条

Argument Description Default
x 负数向左滑动,正数向右滑动 None
y 负数向下滑动,正数向上滑动 None

getScreenSize(): 获取屏幕分辨率 (width * height)

现在所有的积木模块都已经准备好,我们要做的事情就是把积木组装起来。因为手机端基于浏览器 html5 实现,所以需要搭建一个简单的 web app,可以基于 Koa,启动一个 web 服务非常简单,启动 web server 的同时初始化好 socket.io 服务。在 html 页面上引入 hammer.js 实现手势的识别,并通过 socket.io 连接把手势数据实时传输到服务端,服务端解析数据包,并通过 robotjs 驱动鼠标,实现对鼠标的控制。

在进行 socket.io 通信时可以参照官方文档进行性能优化,实现最佳通信性能。同时每次通信时,只传输必要的数据,减少冗余数据,数据经过二进制序列化等都可以帮助减少数据包大小,减少数据包时延,提升操控体验。

mobile-touchpad

mobile-touchpad 项目已开源,项目地址 https://github.com/huoyijie/mobile-touchpad/

安装

1.检查 Node 版本,高于此版本应该都是可以的

$ node -v
v14.16.0

$ npm -v
6.14.13

2.克隆代码

git clone https://github.com/huoyijie/mobile-touchpad.git

3.安装依赖

npm i

4.运行服务端

npm start

5.打开手机浏览器,访问 http://ip:3000,其中 ip 为电脑 ip 地址

等待页面加载完成后,就可以通过手机触控操作电脑了。