一文讲透鼠标及触控板开发、使用技巧
[TOC]
开发白板过程中,画布平移缩放操作是使用很频繁且最基础的交互功能。由于需求排期一直未实现该功能,导致白板开发使用过程中体验差点意思,有一天我终于忍不了了,花了一个晚上研究了一下相关的内容。所以有了以下实践。
一、在白板类绘图产品中,平移缩放大部分都是依靠鼠标/触控板操作完成的。这里不可避免的要处理鼠标事件相关的代码逻辑。针对不同设备需要兼容处理不同的逻辑,尤其在使用 mac 触控板时左滑返回上一页的操作很干扰画图,会导致浏览器页面后退,需要禁用该操作。
二、在我们日常工作生活中,几乎每天都需要用鼠标或者触控板。但是发现周围大部分用 mac 的同事使用触控板的方式都不太高效,还是使用普通鼠标那一套操作。不愿更新自己的使用习惯。
针对以上原因,我要提问了:我们真的会运用鼠标和触控板吗?
这个问题包含两层含义:
- 作为 mac 用户:触控板我们使用习惯是否高效。
- 作为前端开发者:触控板相关的需求我们能否实现。
接下来我们带着问题来寻找答案。
TL;DR
看好多国外的文章,太长了都会有 TL;DR(Too Long; Didn’t Read.),我认为那不是 Too Long;Didn’t Read,而是 Too Lazy; Didn’t Read. 😂 所以我也先把结论写在这里。
通过本文能学到的知识:
- 鼠标模式
- 鼠标滚轮缩放画布
- 右键长按拖拽平移画布
- 触控板模式
- 双指移动平移画布
- 双指捏合缩放画布
- 禁用双指轻扫在页面间切换
- 禁用智能缩放
- 触控板的高级用法
下面开始正文:
对 mac 操作很溜的选手可以忽略“触控板使用”这节。 但是我发现身边大部分使用 mac 的同事都不怎么会熟练使用触控板的高级功能,还仅仅停留在能用的阶段。我个人推荐你阅读下升级自己的使用习惯。
-–»> 忽略开始 «<—
1 基础概念
1.1 鼠标、触控板
如下图所示分别是:Magic Trackpad 妙控板(文中统称触控板)、Magic Mouse 妙控鼠标、 普通鼠标。
(Magic Trackpad 妙控板 / Magic Mouse 妙控鼠标 / 普通鼠标)
本节所说的区别特指Apple 触控设备和普通鼠标的交互区别。因为妙控板和妙控鼠标交互形式是一致的,都是单指、双指点击或者滑动以及多指滑动,而普通鼠标则是按键点击与滚轮滚动与 Apple 触控设备有着明显的区别。
1.2 点击和手势
相信现在工作的大家对鼠标的的操作习惯,都来源于 Windows 时代的人机交互。
而 Apple 设备改进了这一交互习惯。主要还是得益于硬件设备支持了多点触控。有了多点触控就可以增加更多的手势交互。当年在鼠标时代就用过很多浏览器插件,支持鼠标手势,通过鼠标画一个预先设置好的图形,就可以执行某些具体的操作,但是用鼠标画图还是有些难以操作。
我们直接来看 Apple 的手势操作。
触控板常用到的手势操作有:
滚动缩放类:
- 放大缩小
- 智能缩放
这里提出一个问题:明明是缩放,为什么他们的设置项要叫滚动缩放?
更多手势类:
- 常规点击操作
- 在页面之间轻扫
- 在全屏幕 App 之间轻扫
- 调度中心
- App Exposé
- 启动台
- 显示桌面
2 触控板独有功能以及推荐用法
这点是我要重点推荐的,观察到大家平时操作触控板拖拽窗口,或者拖拽文件基本都是以下步骤:
- 触控板移动光标到指定位置;
- 单指使劲按下触控板;
- 开始拖动文件,此时还的一直使劲按着触控板;
- 拖到指定位置松手。
到第 2、3 步的时候体验太糟糕了。手指既要向下用力,又要向左右用力滑动,一不小心松劲了,拖着的文件又跑回原来的地方了,或者压根拖到了不该去的地方。真是难受。
使用了下面推荐的方法之后,你就可以随意拖动文件,而且毫不费力,
2.1 触控板三指拖移
设置方法如下:
- 设置 > 辅助功能 > 指针控制 > 鼠标与触控板 > 触控板选项 (触控板三指拖移设置)
- 如果懒得找那么深的设置,也可以直接搜索:“拖移”。 可以看到 Apple 给他的定位描述 “让鼠标和触控板更易于使用”
提醒:由于三指被占用,原来触控板里的“更多手势”里凡是用到三指的都要改成四指,避免冲突。 接下来几天你的操作可能会特别难受,但是习惯的升级总是要经历一个过程的。
以上就是触控板的最便于使用的方法。
-–»> 忽略结束 «<—
接下来就是代码部分了。
-————————-我是分割线————————–
3 鼠标触控板开发
文章开头提出的第二个问题:我们要实现触控板/鼠标的平移缩放功能该如何实现呢?相信大部分前端开发者第一反应想到监听鼠标滚轮事件。没错,就是运用鼠标滚轮事件。
此时我又要提问了:
- 触控板的双指捏合缩放页面怎么实现呢?
- 触控板的双指任意方向滑动平移页面该怎么实现呢?
- 触控板又该如何禁用双指轻扫在页面间切换?
这些如果你还不太清楚,那就的接着往下看了。
下面我们继续带着问题来找答案。以下是我找答案的过程:
- 本着面向 Google 编程的思想,在最开始我先 Google 了一通 “触控板 禁用 双指 后退 返回 trackpad disable“ 等等关键词,搜索结果都是移动端页面触控手势,触控板的文章少得可怜,基本都是介绍触控板的使用,代码相关的一无所获。
- 后来我在想会不会触控板有我不知道的双指事件,我又去
developer.apple.com
一通搜索也是只有 Mouse and Trackpad 的功能介绍,在深入就到 Appkit 了显然这条路也不对。 - 一筹莫展之下
stack overflow
永远不会让你失望。在这里找到了一个问题: Detect touchpad vs mouse in Javascript ,心想反正也要判断触控板和鼠标,那就抄一下这个答案吧。抄答案时候发现基本用到的都是mousewheel
事件。 - 所以 mdn 接着找
mousewheel
资料,发现mousewheel: This feature is no longer recommended.
推荐用wheel
,似乎我们已经找到了解决问题的方法。
用到的对象主要是 WheelEvent
。除他之外还有几个关联的事件对象要熟悉:MouseEvent
,UIEvent
,Event
。这些事件对象都是依次继承下来的。
到这里答案基本出来了解决上面的问题主要运用的就是:wheel
鼠标滚轮事件。到这里也回答了上面的问题:为何双指缩放等设置项会归类到滚动缩放下,因为他们的实现都是使用了 wheel
事件。
那我们就细细研究一下 wheel
事件,先看他的几个对我们来说有用的属性:
WheelEvent.deltaX
只读:返回 double 值,该值表示滚轮的横向滚动量。WheelEvent.deltaY
只读:返回 double 值,该值表示滚轮的纵向滚动量。
从事件属性可以看出该事件主要用来监听鼠标滚轮的滚动,并可以拿到滚动量。 为了搞的更清楚这几个属性所代表的意义,接下来我做了个小实验,来探究 delta
的变化规律。
3.1 wheel 小实验
实验说明: 页面有两个 div
,内部 div
比外部的大,通过监听滚轮事件滚动寻找 deltaX、deltaY
的规律。 同时将 scrollTop、scrollLeft
打印出来进行参照看 div
的滚动方向。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
/>
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>你不知道的 Trackpad</title>
<style>
body {
background-color: #25252580;
overflow: hidden;
}
#root {
position: fixed;
top: calc(50% - 150px);
left: calc(50% - 150px);
width: 300px;
height: 300px;
/* 任意方向滚动 */
overflow: scroll;
/* 纵向滚动效果 */
/*overflow-x: hidden;*/
/*overflow-y: scroll;*/
/* 横向滚动效果 */
/*overflow-x: scroll;*/
/*overflow-y: hidden;*/
}
h1 {
position: absolute;
top: 0;
left: 0;
}
#container {
width: 1000px;
height: 1000px;
/* 背景色渐变 */
background: linear-gradient(
217deg,
rgba(255, 0, 0, 0.8),
rgba(255, 0, 0, 0) 70.71%
), linear-gradient(
127deg,
rgba(0, 255, 0, 0.8),
rgba(0, 255, 0, 0) 70.71%
), linear-gradient(336deg, rgba(0, 0, 255, 0.8), rgba(0, 0, 255, 0) 70.71%);
}
</style>
</head>
<body>
灰色的都是body
<div id="root">
<h1>你不知道的 Trackpad</h1>
<div id="container"></div>
</div>
<script>
document.addEventListener(
"wheel",
function (e) {
e.stopPropagation();
e.preventDefault();
return false;
},
{ passive: false }
);
document.getElementById("root").addEventListener(
"wheel",
function (e) {
e.stopPropagation();
logInfo(e, "root");
isTrackpadOrMouse(e);
},
{ passive: false }
);
let hnum = 1;
let vnum = 1;
// 临时存放日志,方便以表格展示方便对比结果
let temp = [];
// 是否一直打印日志
let always = false;
/**
* 重点看这几个属性:deltaX、deltaY、ctrlKey
*/
function logInfo(e, name) {
let info = {
name: name,
button: e.button,
ctrlKey: e.ctrlKey,
// altKey: e.altKey,
// metaKey: e.metaKey,
shiftKey: e.shiftKey,
// which: e.which,
// clientX: e.clientX,
// clientY: e.clientY,
deltaMode: e.deltaMode,
deltaX: e.deltaX,
deltaY: e.deltaY,
// deltaZ: e.deltaZ,
// layerX: e.layerX,
// layerY: e.layerY,
// offsetX: e.offsetX,
// offsetY: e.offsetY,
// wheelDelta: e.wheelDelta,
wheelDeltaX: e.wheelDeltaX,
wheelDeltaY: e.wheelDeltaY,
scrollTop: document.getElementById(name).scrollTop,
scrollLeft: document.getElementById(name).scrollLeft,
// x: e.x,
// y: e.y,
};
if (always || hnum % 10 === 0 || vnum % 10 === 0) {
temp.push(info);
}
// 垂直滚动时
if (e.deltaY > 0) {
vnum += 1;
} else if (e.deltaY < 0) {
vnum -= 1;
}
// 水平滚动时
if (e.deltaX > 0) {
hnum += 1;
} else if (e.deltaX < 0) {
hnum -= 1;
}
if (always || hnum === 0 || vnum === 0) {
console.table(always ? [info] : temp);
}
}
/**
* 判断是鼠标或者触控板
* @param e
* @return {boolean}
*/
function isTrackpadOrMouse(e) {
let isTrackpad = false;
if (e.wheelDeltaY) {
if (e.wheelDeltaY === e.deltaY * -3) {
isTrackpad = true;
}
} else if (e.deltaMode === 0) {
isTrackpad = true;
}
console.log(isTrackpad ? "Trackpad detected" : "Mousewheel detected");
return isTrackpad;
}
</script>
</body>
</html>
实验效果图:
3.2 实验结论
通过实验发现:
3.2.1 触控板
- 平移 是判断
deltaX
、deltaY
正负
deltaX
连续为正数:向左 👈deltaX
连续为负数:向右 👉deltaY
连续为正数:向上 👆deltaY
连续为负数:向下 👇
为什么要强调连续,因为触控板比较灵敏(灵敏度可调),而且滚动会有惯性(惯性也可设置)。偶尔一次正负变化不能说明确切方向,必须连续才能说明方向变化。
灵敏度、惯性设置还是在辅助功能这里:
- 缩放 是判断
deltaY
+ctrlKey
的值
ctrlKey = true
&deltaY
连续为正数:向内ctrlKey = true
&deltaY
连续为负数:向外
3.2.2 鼠标
普通鼠标滚轮一般只有上下两个方向,所以鼠标模式下:
- 缩放 是滚轮滚动
- 平移 是长按右键拖拽
我的实验代码为了简单没有做连续差值计算,只是简单判断了正负,连续的更加严谨。
3.3 禁用触控板双指轻扫返回、智能缩放
平移缩放功能实现了,还差怎么禁用智能缩放,和清扫返回上一页。 下图这个箭头你一定见过,平时用着挺方便,但是在画图软件里,想平移画布结果页面跳转了,太影响操作。所以需要掉它。
而智能缩放更像是一个放大镜,他与浏览器的缩放还不一样。 浏览器的缩放是将页面的每个元素都等比例放大。 智能缩放的放大就像缩放图片一样,页面超出浏览器窗口的元素都看不见了无法操作。 所以也需要禁掉智能缩放。
在操作部分我们说过,双指轻扫返回、智能缩放都归类在滚动里,并且上面实验也证实他也是使用了 wheel
事件只不过事件绑定在了 document
上。所以我们只需要禁止 document
上的 wheel
事件默认行为就可以了禁止这些操作。
addEventListener
的passive
属性
看 MDN 文档说明:
passive: Boolean
,设置为true
时,表示listener
永远不会调用preventDefault()
。如果listener
仍然调用了这个函数,客户端将会忽略它并抛出一个控制台警告。
在 addEventListener
中 passive
默认是 true
,只有 passive:false
,e.preventDefault()
才会生效。
对 document
拦截了滚轮事件默认行为后,因为该事件会传播冒泡,所以需要注意页面上其他的子元素,如果有滚动条之类的也会失效,需要在子元素上阻止冒泡。
document.addEventListener(
"wheel",
function (e) {
e.stopPropagation();
// 所以实际代码中要在这里判断,只有某些情况才禁用默认行为。
e.preventDefault();
return false;
},
{ passive: false }
);
3.4 注意点
注意事项: 请勿想当然依据滚轮方向(即该事件的各 delta 属性值)来推断文档内容的滚动方向,因标准未定义滚轮事件具体会引发什么样的行为,引发的行为实际上是各浏览器自行定义的。即便滚轮事件引发了文档内容的滚动行为,也不表示滚轮方向和文档内容的滚动方向一定相同。因而通过该滚轮事件获知文档内容滚动方向的方法并不可靠。要获取文档内容的滚动方向,可在文档内容滚动事件(
scroll (en-US)
)中监听scrollLeft
和scrollTop
二值变化情况,即可推断出滚动方向了。
详细的属性说明看 MDN wheel 事件文档 ,不在这里粘文档了。
到这里基本上该有的功能就都实现了。本文实现需求在文章开头已经给出这里不再赘述。 这篇文章到此也就结束了。希望会对你有所帮助。同时感谢以下博客主为我们做出的探索与尝试。