[TOC]
## 触摸事件
为了给触摸界面提供有力支持, 触摸事件提供了响应用户对触摸屏或者触摸板上操作的能力.
### 接口
#### touchEvent
TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等。
每 个 Touch 对象代表一个触点; 每个触点都由其位置,大小,形状,压力大小,和目标 element 描述。 TouchList 对象代表多个触点的一个列表.
****构造函数
TouchEvent\(\)
> 创建一个TouchEvent对象。
**属性列表**
`TouchEventUIEvent`的属性继承了 `UIEvent`和`Event` 。
* `TouchEvent.altKey:`只读,布尔值,指明触摸事件触发时,键盘 alt 键是否被按下。
* `TouchEvent.changedTouches:`只读,一个`TouchListTouch`对象,包含了代表所有从上一次触摸事件到此次事件过程中,状态发生了改变的触点的对象。
* `TouchEvent.ctrlKey:`只读,布尔值,指明触摸事件触发时,键盘 ctrl 键是否被按下。
* `TouchEvent.metaKey:`只读,布尔值,指明触摸事件触发时,键盘 meta 键 (Wikipedia - meta Key)是否被按下。
* `TouchEvent.shiftKey:`只读,布尔值,指明触摸事件触发时,键盘 shift 键是否被按下。
* `TouchEvent.targetTouches:`只读,一个`TouchList`对象,是包含了如下触点的`Touch`对象:触摸起始于当前事件的目标`element`上,并且仍然没有离开触摸平面的触点。
* `TouchEvent.touches:`只读,一 个`TouchList`对象,包含了所有当前接触触摸平面的触点的`Touch`对象,无论它们的起始于哪个`element`上,也无论它们状态是否发生了变化。
**touches、targetTouches和changedTouches区别**
1. 用一个手指接触屏幕,触发事件,此时这三个属性有相同的值。
2. 用第二个手指接触屏幕,此时,touches有两个元素,每个手指触摸点为一个值。当两个手指触摸相同元素时, targetTouches和touches的值相同,否则targetTouches 只有一个值。changedTouches此时只有一个值, 为第二个手指的触摸点,因为第二个手指是引发事件的原因
3. 用两个手指同时接触屏幕,此时changedTouches有两个值,每一个手指的触摸点都有一个值
4. 手指滑动时,三个值都会发生变化
5. 一个手指离开屏幕,touches和targetTouches中对应的元素会同时移除,而changedTouches仍然会存在元素。
6. 手指都离开屏幕之后,touches和targetTouches中将不会再有值,changedTouches还会有一个值,此值为最后一个离开屏幕的手指的接触点。
****触点坐标选取****
* touchstart和touchmove使用:e.targetTouches\[0\].pageX 或 \(jquery\)e.originalEvent.targetTouches\[0\].pageX
* touchend使用:e.changedTouches\[0\].pageX 或 \(jquery\)e.originalEvent.changedTouches\[0\].pageX
**触摸事件的类型**
为了区别触摸相关的状态改变,存在多种类型的触摸事件。可以通过检查触摸事件的[`TouchEvent.type`](https://developer.mozilla.org/zh-CN/docs/Web/API/Event/type)属性来确定当前事件属于哪种类型
> **注意:**
>
> 在很多情况下,触摸事件和鼠标事件会同时被触发(目的是让没有对触摸设备优化的代码仍然可以在触摸设备上正常工作)。如果你使用了触摸事件,可以调用[`event.preventDefault()`](https://developer.mozilla.org/zh-CN/docs/Web/API/Event/preventDefault)来阻止鼠标事件被触发。
#### touchstart
当用户在触摸平面上放置了一个触点时触发。事件的目标[`element`](https://developer.mozilla.org/zh-CN/docs/Web/API/Element)将是触点位置上的那个目标[`element`](https://developer.mozilla.org/zh-CN/docs/Web/API/Element)
#### touchend
当一个触点被用户从触摸平面上移除(当用户将一个手指离开触摸平面)时触发。当触点移出触摸平面的边界时也将触发。例如用户将手指划出屏幕边缘。
事件的目标[`element`](https://developer.mozilla.org/zh-CN/docs/Web/API/Element)和这个`touchend`事件对应的`touchstart 事件的目标`[`element`](https://developer.mozilla.org/zh-CN/docs/Web/API/Element)相同,哪怕`touchend`事件触发时,触点已经移出了该[`element`](https://developer.mozilla.org/zh-CN/docs/Web/API/Element)。
已经被从触摸平面上移除的触点,可以在`changedTouches 属性定义的`[`TouchList`](https://developer.mozilla.org/zh-CN/docs/Web/API/TouchList)中找到。
#### touchmove
当用户在触摸平面上移动触点时触发。事件的目标[`element`](https://developer.mozilla.org/zh-CN/docs/Web/API/Element)和这个`touchmove`事件对应的`touchstart 事件的目标`[`element`](https://developer.mozilla.org/zh-CN/docs/Web/API/Element)相同,哪怕当`touchmove` 事件触发时,触点已经移出了该[`element`](https://developer.mozilla.org/zh-CN/docs/Web/API/Element)。
当触点的半径、旋转角度以及压力大小发生变化时,也将触发此事件。
> 不同浏览器上 touchmove 事件的触发频率并不相同。这个触发频率还和硬件设备的性能有关。因此决不能让程序的运作依赖于某个特定的触发频率。
#### touchcancel
当触点由于某些原因被中断时触发。有几种可能的原因如下(具体的原因根据不同的设备和浏览器有所不同):
* 由于某个事件取消了触摸:例如触摸过程被一个模态的弹出框打断。
* 触点离开了文档窗口,而进入了浏览器的界面元素、插件或者其他外部内容区域。
* 当用户产生的触点个数超过了设备支持的个数,从而导致[`TouchList`](https://developer.mozilla.org/zh-CN/docs/Web/API/TouchList)中最早的[`Touch`](https://developer.mozilla.org/zh-CN/docs/Web/API/Touch)对象被取消。
#### Touch
**Touch**对象表示在触控设备上的触摸点。通常是指手指或者触控笔在触屏设备或者触摸板上的操作。
对象属性 [`Touch.radiusX`](https://developer.mozilla.org/zh-CN/docs/Web/API/Touch/radiusX),[`Touch.radiusY`](https://developer.mozilla.org/zh-CN/docs/Web/API/Touch/radiusY), 和[`Touch.rotationAngle`](https://developer.mozilla.org/zh-CN/docs/Web/API/Touch/rotationAngle)表示用户触摸操作所作用的区域,即_触摸区域_。这些属性对于处理类似于手指触摸之类的不精确操作很有帮助。这些属性可以表示出一个尽可能匹配触控区域的椭圆形(例如用户的指尖触控)。
#### TouchList
一个`TouchList`代表一个触摸平面上所有触点的列表; 举例来讲, 如果一个用户用三根手指接触屏幕\(或者触控板\), 与之相关的`TouchList`对于每根手指都会生成一个[`Touch`](https://developer.mozilla.org/zh-CN/docs/Web/API/Touch)对象, 共计三个.
### 属性 {#属性}
* `TouchList.length:`返回`TouchList`中对象的数量。**只读属性**
### 方法 {#方法}
* `TouchList.identified():`列表中标示符与指定值匹配的第一个`Touch`对象会被返回.
* `TouchList.item():`返回列表中以指定值作为索引的对象。你也可以使用数组的语法来引用`TouchList`\(`touchList[x]`\)。
### 附加小贴士
这部分提供了一些如何在web应用中处理触摸事件的小贴士。
#### 处理点击
当处理一系列触摸操作时,为了阻止鼠标事件的触发,可以在`touchstart` 或第一个 `touchmove中调用`preventDefault\(\),更适合的做法是在touchmove中调用preventDefault\(\)。
这样做的话,鼠标事件仍然会被触发,相关的如链接等就可以继续工作。有些框架采取了一个替代方案,使用触摸事件代替鼠标事件来达到相同的目的。 \(下面这个例子过于简单,也可能产生奇怪的行为。这里仅仅作为一个引导\).
```
function onTouch(evt) {
evt.preventDefault();
if (evt.touches.length > 1 || (evt.type == "touchend" && evt.touches.length > 0))
return;
var newEvt = document.createEvent("MouseEvents");
var type = null;
var touch = null;
switch (event.type) {
case "touchstart": type = "mousedown"; touch = event.changedTouches[[0];
case "touchmove": type = "mousemove"; touch = event.changedTouches[[0];
case "touchend": type = "mouseup"; touch = event.changedTouches[0];
}
newEvt.initMouseEvent(type, true, true, event.originalTarget.ownerDocument.defaultView, 0,
touch.screenX, touch.screenY, touch.clientX, touch.clientY,
evt.ctrlKey, evt.altKey, evt.shirtKey, evt.metaKey, 0, null);
event.originalTarget.dispatchEvent(newEvt);
}
```
#### 只在第二次触摸时调用 preventDefault\(\) {#只在第二次触摸时调用_preventDefault()}
`有一种`技术可以`在触摸时,阻止页面上发生pinchZoom(捏拉缩放)之类操作,他是通过在一系列触摸操作中,对第二个触摸调用preventDefault()函数`. 这个行为未在标准中被定义,不同浏览器的表现也不同\(IOS会阻止缩放但扔允许双指平移,Android会允许缩放,但阻止平移。这两种行为在Opera与Firefox中均被阻止。\).因此现在不建议使用此方式, 而是通过meta viewport 来阻止页面缩放.
## 使用地理位置定位
**地理位置 API**允许用户向 Web 应用程序提供他们的位置。出于隐私考虑,报告地理位置前会先请求用户许可。
### geolocation 对象
地理位置 API 通过`navigator.geolocation`提供。
如果该对象存在,那么地理位置服务可用。
```
if ("geolocation" in navigator) {
/* 地理位置服务可用 */
} else {
/* 地理位置服务不可用 */
}
```
### 获取当前定位
您可以调用`getCurrentPosition()`函数获取用户当前定位位置。这会异步地请求获取用户位置,并查询定位硬件来获取最新信息。当定位被确定后,定义的回调函数就会被执行。您可以选择性地提供第二个回调函数,当有错误时会被执行。第三个参数也是可选的,您可以通过该对象参数设定最长可接受的定位返回时间、等待请求的时间和是否获取高精度定位。
#### getCurrentPosition
```
navigator.geolocation.getCurrentPosition(success, error, options)
```
**参数**
* success:成功得到位置信息时的回调函数,使用`Position`对象作为唯一的参数。
* error:可选,获取位置信息失败时的回调函数,使用`PositionError`对象作为唯一的参数,这是一个可选项。
* options:可选,一个可选的`PositionOptions`对象。
```
var options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
function success(pos) {
var crd = pos.coords;
console.log('Your current position is:');
console.log('Latitude : ' + crd.latitude);
console.log('Longitude: ' + crd.longitude);
console.log('More or less ' + crd.accuracy + ' meters.');
};
function error(err) {
console.warn('ERROR(' + err.code + '): ' + err.message);
};
navigator.geolocation.getCurrentPosition(success, error, options);
```
> **注意:**
>
> 默认情况下,`getCurrentPosition()getCurrentPosition()`会尽快返回一个低精度结果,这在您不关心准确度只关心快速获取结果的情况下很有用。有 GPS 的设备可能需要一分钟或更久来获取 GPS 定位,在这种情况下会返回低精度数据(基于 IP 的定位或 Wi-Fi 定位)。
#### 监视定位
您可以设定一个回调函数来响应定位数据发生的变更(设备发生了移动,或获取到了更高精度的地理位置信息)。您可以通过`watchPosition()getCurrentPosition()`函数实现该功能。它与`getCurrentPosition()`接受相同的参数,但回调函数会被调用多次。错误回调函数与中一样是可选的,也会被多次调用。
> **注意:**
>
> 您可以直接调用`watchPosition()getCurrentPosition()`函数,不需要先调用函数。
`watchPosition()clearWatch()`函数会返回一个 ID,唯一地标记该位置监视器。您可以将这个 ID 传给函数来停止监视用户位置。
```
navigator.geolocation.clearWatch(watchID);
```
#### 调整返回结果
`getCurrentPosition()watchPosition()`和都接受一个成功回调、一个可选的失败回调和一个可选的`PositionOptions`对象。
对`watchPosition`的调用类似于这样:
```
function geo_success(position) {
do_something(position.coords.latitude, position.coords.longitude);
}
function geo_error() {
alert("Sorry, no position available.");
}
var geo_options = {
enableHighAccuracy: true,
maximumAge : 30000,
timeout : 27000
};
var wpid = navigator.geolocation.watchPosition(geo_success, geo_error, geo_options);
```
### 描述位置
用户的位置由一个包含`Coordinates`对象的`Position`对象描述。
### 处理错误
`getCurrentPosition()`或`watchPosition()`的错误回调函数以`PositionError`为第一个参数。
```
function errorCallback(error) {
alert('ERROR(' + error.code + '): ' + error.message);
};
```
## 检测设备方向
越来越多地,基于web的设备能够确定它们的方向; 也就是说,它们可以报告指示相对于重力拉力的它们的取向的改变的数据。特别地,通过这些数据,像手机等一些手持设备可以实现自动调整旋转角度来保持显示直立,以及当设备旋转到宽屏时(宽度大于高度),自动提供宽屏的网页效果。
有两种Javascript事件负责处理设备方向信息。第一种是[`DeviceOrientationEvent`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceOrientationEvent),它会在加速度传感器检测到设备在方向上产生变化时触发。通过处理该事件传来的数据信息,使针对由于用户移动设备引起旋转和仰角变化的行为设计交互响应成为可能。
第二种是[`DeviceMotionEvent`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceMotionEvent),它会在加速度发生改变时触发。跟[`DeviceOrientationEvent`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceOrientationEvent)不同,[`DeviceMotionEvent`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceMotionEvent)监听的是加速度的变化而不是方向。传感器通常都具有监听[`DeviceMotionEvent`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceMotionEvent)的能力,包括笔记本中用于保护移动存储设备的传感器。而能监听[`DeviceOrientationEvent`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceOrientationEvent)事件的传感器更多出现在移动设备中。
### 处理方向(orientation)事件 {#处理方向(orientation)事件}
要接收设备方向变化信息,你只需要注册监听[`deviceorientation`](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/deviceorientation)事件:
```
window.addEventListene("deviceorientation", handleOrientation,true);
```
注册完事件监听处理函数后(对应这个例子中的handleOrientation),监听函数会定期地接收到最新的设备方向数据。
方向事件对象中包含四个值:
[`DeviceOrientationEvent.absolute`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceOrientationEvent/absolute)
[`DeviceOrientationEvent.alpha`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceOrientationEvent/alpha)
[`DeviceOrientationEvent.beta`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceOrientationEvent/beta)
[`DeviceOrientationEvent.gamma`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceOrientationEvent/gamma)
下面是一个事件处理函数的例子:
```
function handleOrientation(orientData) {
var absolute = orientData.absolute;
var alpha = orientData.alpha;
var beta = orientData.beta;
var gamma = orientData.gamma;
// Do stuff with the new orientation data
}
```
#### 相关值解释
关于每一个轴的记录值表示的是相对于标准的坐标系,设备在某一个给定轴上的旋转量。[Orientation and motion data explained](https://developer.mozilla.org/en-US/DOM/Orientation_and_motion_data_explained) 这篇文章有更详细的描述,下面是对这篇文章的总结。
* [`DeviceOrientationEvent.alpha`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceOrientationEvent/alpha)
表示设备沿z轴上的旋转角度,范围为0~360。
* [`DeviceOrientationEvent.beta`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceOrientationEvent/beta)
表示设备在x轴上的旋转角度,范围为-180~180。它描述的是设备由前向后旋转的情况。
* [`DeviceOrientationEvent.gamma`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceOrientationEvent/gamma)
表示设备在y轴上的旋转角度,范围为-90~90。它描述的是设备由左向右旋转的情况。
#### 例子
这个例子会成功运行在支持检测自己方向的设备中的支持[`deviceorientation`](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/deviceorientation)事件的浏览器中。
想象一下花园(garden)中的一个球(ball):
```
<div class="garden">
<div class="ball"></div>
</div>
<pre class="output"></pre>
```
花园只有200px宽(很小对吧),球在中央:
```
.garden {
position: relative;
width : 200px;
height: 200px;
border: 5px solid #CCC;
border-radius: 10px;
}
.ball {
position: absolute;
top : 90px;
left : 90px;
width : 20px;
height: 20px;
background: green;
border-radius: 100%;
}
```
现在,如果我们移动设备,球将随之移动:
```
var ball = document.querySelector('.ball');
var garden = document.querySelector('.garden');
var output = document.querySelector('.output');
var maxX = garden.clientWidth - ball.clientWidth;
var maxY = garden.clientHeight - ball.clientHeight;
function handleOrientation(event) {
var x = event.beta; // In degree in the range [-180,180]
var y = event.gamma; // In degree in the range [-90,90]
output.innerHTML = "beta : " + x + "\n";
output.innerHTML += "gamma: " + y + "\n";
// Because we don't want to have the device upside down
// We constrain the x value to the range [-90,90]
if (x > 90) { x = 90};
if (x < -90) { x = -90};
// To make computation easier we shift the range of
// x and y to [0,180]
x += 90;
y += 90;
// 10 is half the size of the ball
// It center the positioning point to the center of the ball
ball.style.top = (maxX*x/180 - 10) + "px";
ball.style.left = (maxY*y/180 - 10) + "px";
}
window.addEventListener('deviceorientation', handleOrientation);
```
> **警告:**
>
> Chrome 和 Firefox 处理角度的机制不同,所以某些轴上的方向是相反的。
### 处理移动(motion)事件
移动事件的处理跟方向事件是一样的,除了他们有自己的事件名:[`devicemotion`](https://developer.mozilla.org/zh-CN/docs/Web/Reference/Events/devicemotion)
```
window.addEventListener("devicemotion", handleMotion, true);
```
真正不同的是做为参数传递给HandleMotion函数的[`DeviceMotionEvent`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceMotionEvent)对象所包含的信息。
这个对象包含四个属性:
* [`DeviceMotionEvent.acceleration`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceMotionEvent/acceleration)
* [`DeviceMotionEvent.accelerationIncludingGravity`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceMotionEvent/accelerationIncludingGravity)
* [`DeviceMotionEvent.rotationRate`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceMotionEvent/rotationRate)
* [`DeviceMotionEvent.interval`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceMotionEvent/interval)
#### 相关值解释
[`DeviceMotionEvent`](https://developer.mozilla.org/zh-CN/docs/Web/API/DeviceMotionEvent)对象提供给web开发者设备在位置和方向上的改变速度的相关信息。这些变化信息是通过三个轴来体现的。([Orientation and motion data explained](https://developer.mozilla.org/en-US/docs/Web/Guide/DOM/Events/Orientation_and_motion_data_explained)有更详细的说明。)
[`acceleration`](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent.acceleration) 和 [`accelerationIncludingGravity`](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent.accelerationIncludingGravity),都包含下面三个轴:
* `x`: 西向东
* `y`: 南向北
* `z`: 垂直地面
[`rotationRate`](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent.rotationRate), the situation is a bit different; the information corresponds to the following in each case:
[`rotationRate`](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent.rotationRate)`的情况有点不同:`
* `alpha`: 设备沿着垂直屏幕的轴的旋转速率 \(桌面设备相对于键盘\)
* `beta`: 设备沿着屏幕左至右方向的轴的旋转速率\(桌面设备相对于键盘\)
* `gamma`: 设备沿着屏幕下至上方向的轴的旋转速率\(桌面设备相对于键盘\)
最后,[`interval`](https://developer.mozilla.org/en-US/docs/Web/API/DeviceMotionEvent.interval) 表示的是从设备获取数据的频率,单位是毫秒。
## PointerLock API
**指针锁定**\(以前叫做鼠标锁定\) 提供了一种输入方法,这种方法是基于鼠标随着时间推移的运动的(也就是,deltas),而不仅是鼠标光标的绝对位置。通过它可以访问原始的鼠标运动,把鼠标事件的目标锁定到一个单独的元素,这就消除了鼠标在一个单独的方向上到底可以移动多远这方面的限制,并从视图中删去光标。
这个 API 对于需要大量的鼠标输入来控制运动,旋转物体,以及更改项目的应用程序来说非常有用。对高度视觉化的应用程序尤其重要,例如那些使用第一人称视角的应用程序,以及 3D 视图和建模。
举例来说,你可以创建让你的用户简单地通过移动鼠标而不需要点击任何按钮就可以控制视角的应用。那么这些按钮就可以被用作其他动作。这类鼠标输入对于查看地图,卫星图像,或者第一人称场景(例如在一个游戏中或者一个全景视频中)是非常方便使用的。
即使在光标移到浏览器或者屏幕区域之外,指针锁定也能够让你访问鼠标事件。例如,你的用户可以通过不断地移动鼠标来持续旋转或操纵一个 3D 模型。如果没有指针锁定的话,这些旋转或操纵会在指针到达浏览器或者屏幕边缘的那一刻停止。尤其是游戏玩家将会因为此功能而兴奋不已,因为他们可以疯狂地点击按钮,来回地滑动鼠标光标,而不必担心离开了游戏区域,进而不小心误点到另外一个应用程序上,结果将鼠标焦点移离了游戏。
### 基本概念
指针锁定和[鼠标捕获](https://developer.mozilla.org/en/DOM/element.setCapture)有关。鼠标捕获在一个鼠标被拖曳时可以向一个目标元素持续传递有关事件,但是当鼠标按钮被放开时就会停止。指针锁定和鼠标捕获在以下方面有所不同:
* 它是持久性的。指针锁定不释放鼠标,直到作出一个显式的 API 调用或是用户使用一个专门的释放手势。
* 它不局限于浏览器或者屏幕边界。
* 它持续发送事件,而不管鼠标按钮状态如何。
* 它隐藏光标。
### 方法/属性 概述
#### pointerLockElement 属性
指当前页面触发鼠标无限滚动的元素,通常使用语法为:
```
var element = document.pointerLockElement;
```
返回的是一个DOM元素对象,如果当前页面是非鼠标锁定状态,则返回值是null。
#### requestPointerLock 方法
可以让页面进入鼠标锁定状态(鼠标直接消失),鼠标无限滚动,坐标无限变化。通常使用语法为:
```
var element.requestPointerLock();
```
#### exitPointerLock 方法
让页面从鼠标锁定状态退出,通常使用语法为:
```
document.exitPointerLock();
```
浏览器默认支持按下ESC键退出鼠标锁定状态,但是用户有时候更习惯于点击取消等,此时就可以使用`document.exitPointerLock()`进行设置。
#### pointerlockchange 事件
当指针锁定状态改变时 - 例如,当调用`requestPointerLock`,`exitPointerLock`,用户按下 ESC 键,等等。— `pointerlockchange`事件被分发到`document`。 这是一个简单事件所以不包含任何的额外数据。
> 该事件目前在 Firefox 中使用前缀的格式是`mozpointerlockchange`,在 Chrome 中是`webkitpointerlockchange`。
```
document.addEventListener('pointerlockchange', function () {
// ...
}, false);
```
#### pointerlockerror 事件
当调用`requestPointerLock`或`exitPointerLock`而引发错误时, `pointerlockerror`事件被分发到`document`。这是一个简单事件所以不包含任何的额外数据。例如当你试图同时锁定同一个页面的多个<iframe>时候,就会触发这个出错事件。
> 该事件目前在 Firefox 中被加上前缀为`mozpointerlockerror`,在 Chrome 中为`webkitpointerlockerror`。
### 鼠标事件扩展
Pointer lock API 使用 movement 属性扩展了标准的`MouseEvent`。
```
partial interface MouseEvent {
readonly attribute long movementX;
readonly attribute long movementY;
};
```
> movement 属性目前在 Firefox 中被加上前缀为`.mozMovementX`和`.mozMovementY`, 在 Chrome 中为`.webkitMovementX`和`.webkitMovementY`。
#### 锁定状态
当指针锁定被启动之后,正常的`MouseEvent`属性 `clientX`,`clientY`,`screenX`, 和`screenY`,保持不变,就像鼠标没有在移动一样。`movementX`和`movementY`属性持续提供鼠标的位置变化。如果鼠标在一个方向上持续移动,`movementX`和`movementY`的值是没有限制的。不存在鼠标光标的概念,而且光标无法移到窗口之外,而且也不会被屏幕边缘所固定。
#### 未锁定状态
无论鼠标锁定状态是怎样的, `movementX` 和 `movementY` 参数一直有效,并且为了方便起见,甚至在未锁定状态也是有效的。
当鼠标被解除锁定,系统光标可以退出并重新进入浏览器窗口。如果发生这种情况,`movementX` 和 `movementY` 可能会被设置成0。
### iframe 的限制
指针锁定一次只能锁定一个 iframe。如果你锁定了一个 iframe,你不能试图锁定另外一个 iframe 然后把目标转移到这个 iframe 上;指针锁定将会出错。为了避免这一问题,首先解锁那个锁定的 iframe,然后再锁定另外一个。
在 iframe 默认的情况下, "sandboxed" iframes 会阻止指针锁定。避免这种限制的能力,即以属性/值`<iframe sandbox="allow-pointer-lock">`组合的形式 , 有望很快在 Chrome 中出现。
### 实例
```
<button onclick="lockPointer();">锁住它!</button>
<div id="pointer-lock-element"></div>
<script>
// 注意: 截止本文撰写时, 仅有 Mozilla 和 WebKit 支持指针锁定。
// 我们将要使之全屏并指针锁定的元素。
var elem;
document.addEventListener("mousemove", function(e) {
var movementX = e.movementX ||
e.mozMovementX ||
e.webkitMovementX ||
0,
movementY = e.movementY ||
e.mozMovementY ||
e.webkitMovementY ||
0;
// 打印鼠标移动的增量值。
console.log("movementX=" + movementX, "movementY=" + movementY);
}, false);
function fullscreenChange() {
if (document.webkitFullscreenElement === elem ||
document.mozFullscreenElement === elem ||
document.mozFullScreenElement === elem) { // 较旧的 API 大写 'S'.
// 元素进入全屏模式了,现在我们可以请求指针锁定。
elem.requestPointerLock = elem.requestPointerLock ||
elem.mozRequestPointerLock ||
elem.webkitRequestPointerLock;
elem.requestPointerLock();
}
}
document.addEventListener('fullscreenchange', fullscreenChange, false);
document.addEventListener('mozfullscreenchange', fullscreenChange, false);
document.addEventListener('webkitfullscreenchange', fullscreenChange, false);
function pointerLockChange() {
if (document.mozPointerLockElement === elem ||
document.webkitPointerLockElement === elem) {
console.log("指针锁定成功了。");
} else {
console.log("指针锁定已丢失。");
}
}
document.addEventListener('pointerlockchange', pointerLockChange, false);
document.addEventListener('mozpointerlockchange', pointerLockChange, false);
document.addEventListener('webkitpointerlockchange', pointerLockChange, false);
function pointerLockError() {
console.log("锁定指针时出错。");
}
document.addEventListener('pointerlockerror', pointerLockError, false);
document.addEventListener('mozpointerlockerror', pointerLockError, false);
document.addEventListener('webkitpointerlockerror', pointerLockError, false);
function lockPointer() {
elem = document.getElementById("pointer-lock-element");
// 开始于使元素进入全屏模式。目前的实现
// 要求元素在请求指针锁定前要处于全屏模式下
// -- 这在以后可能会发生改变。
elem.requestFullscreen = elem.requestFullscreen ||
elem.mozRequestFullscreen ||
elem.mozRequestFullScreen || // 较旧的 API 把 ‘S’ 大写
elem.webkitRequestFullscreen;
elem.requestFullscreen();
}
</script>
```
- 第一部分 HTML
- meta
- meta标签
- HTML5
- 2.1 语义
- 2.2 通信
- 2.3 离线&存储
- 2.4 多媒体
- 2.5 3D,图像&效果
- 2.6 性能&集成
- 2.7 设备访问
- SEO
- Canvas
- 压缩图片
- 制作圆角矩形
- 全局属性
- 第二部分 CSS
- CSS原理
- 层叠上下文(stacking context)
- 外边距合并
- 块状格式化上下文(BFC)
- 盒模型
- important
- 样式继承
- 层叠
- 属性值处理流程
- 分辨率
- 视口
- CSS API
- grid(未完成)
- flex
- 选择器
- 3D
- Matrix
- AT规则
- line-height 和 vertical-align
- CSS技术
- 居中
- 响应式布局
- 兼容性
- 移动端适配方案
- CSS应用
- CSS Modules(未完成)
- 分层
- 面向对象CSS(未完成)
- 布局
- 三列布局
- 单列等宽,其他多列自适应均匀
- 多列等高
- 圣杯布局
- 双飞翼布局
- 瀑布流
- 1px问题
- 适配iPhoneX
- 横屏适配
- 图片模糊问题
- stylelint
- 第三部分 JavaScript
- JavaScript原理
- 内存空间
- 作用域
- 执行上下文栈
- 变量对象
- 作用域链
- this
- 类型转换
- 闭包(未完成)
- 原型、面向对象
- class和extend
- 继承
- new
- DOM
- Event Loop
- 垃圾回收机制
- 内存泄漏
- 数值存储
- 连等赋值
- 基本类型
- 堆栈溢出
- JavaScriptAPI
- document.referrer
- Promise(未完成)
- Object.create
- 遍历对象属性
- 宽度、高度
- performance
- 位运算
- tostring( ) 与 valueOf( )方法
- JavaScript技术
- 错误
- 异常处理
- 存储
- Cookie与Session
- ES6(未完成)
- Babel转码
- let和const命令
- 变量的解构赋值
- 字符串的扩展
- 正则的扩展
- 数值的扩展
- 数组的扩展
- 函数的扩展
- 对象的扩展
- Symbol
- Set 和 Map 数据结构
- proxy
- Reflect
- module
- AJAX
- ES5
- 严格模式
- JSON
- 数组方法
- 对象方法
- 函数方法
- 服务端推送(未完成)
- JavaScript应用
- 复杂判断
- 3D 全景图
- 重载
- 上传(未完成)
- 上传方式
- 文件格式
- 渲染大量数据
- 图片裁剪
- 斐波那契数列
- 编码
- 数组去重
- 浅拷贝、深拷贝
- instanceof
- 模拟 new
- 防抖
- 节流
- 数组扁平化
- sleep函数
- 模拟bind
- 柯里化
- 零碎知识点
- 第四部分 进阶
- 计算机原理
- 数据结构(未完成)
- 算法(未完成)
- 排序算法
- 冒泡排序
- 选择排序
- 插入排序
- 快速排序
- 搜索算法
- 动态规划
- 二叉树
- 浏览器
- 浏览器结构
- 浏览器工作原理
- HTML解析
- CSS解析
- 渲染树构建
- 布局(Layout)
- 渲染
- 浏览器输入 URL 后发生了什么
- 跨域
- 缓存机制
- reflow(回流)和repaint(重绘)
- 渲染层合并
- 编译(未完成)
- Babel
- 设计模式(未完成)
- 函数式编程(未完成)
- 正则表达式(未完成)
- 性能
- 性能分析
- 性能指标
- 首屏加载
- 优化
- 浏览器层面
- HTTP层面
- 代码层面
- 构建层面
- 移动端首屏优化
- 服务器层面
- bigpipe
- 构建工具
- Gulp
- webpack
- Webpack概念
- Webpack工具
- Webpack优化
- Webpack原理
- 实现loader
- 实现plugin
- tapable
- Webpack打包后代码
- rollup.js
- parcel
- 模块化
- ESM
- 安全
- XSS
- CSRF
- 点击劫持
- 中间人攻击
- 密码存储
- 测试(未完成)
- 单元测试
- E2E测试
- 框架测试
- 样式回归测试
- 异步测试
- 自动化测试
- PWA
- PWA官网
- web app manifest
- service worker
- app install banners
- 调试PWA
- PWA教程
- 框架
- MVVM原理
- Vue
- Vue 饿了么整理
- 样式
- 技巧
- Vue音乐播放器
- Vue源码
- Virtual Dom
- computed原理
- 数组绑定原理
- 双向绑定
- nextTick
- keep-alive
- 导航守卫
- 组件通信
- React
- Diff 算法
- Fiber 原理
- batchUpdate
- React 生命周期
- Redux
- 动画(未完成)
- 异常监控、收集(未完成)
- 数据采集
- Sentry
- 贝塞尔曲线
- 视频
- 服务端渲染
- 服务端渲染的利与弊
- Vue SSR
- React SSR
- 客户端
- 离线包
- 第五部分 网络
- 五层协议
- TCP
- UDP
- HTTP
- 方法
- 首部
- 状态码
- 持久连接
- TLS
- content-type
- Redirect
- CSP
- 请求流程
- HTTP/2 及 HTTP/3
- CDN
- DNS
- HTTPDNS
- 第六部分 服务端
- Linux
- Linux命令
- 权限
- XAMPP
- Node.js
- 安装
- Node模块化
- 设置环境变量
- Node的event loop
- 进程
- 全局对象
- 异步IO与事件驱动
- 文件系统
- Node错误处理
- koa
- koa-compose
- koa-router
- Nginx
- Nginx配置文件
- 代理服务
- 负载均衡
- 获取用户IP
- 解决跨域
- 适配PC与移动环境
- 简单的访问限制
- 页面内容修改
- 图片处理
- 合并请求
- PM2
- MongoDB
- MySQL
- 常用MySql命令
- 自动化(未完成)
- docker
- 创建CLI
- 持续集成
- 持续交付
- 持续部署
- Jenkins
- 部署与发布
- 远程登录服务器
- 增强服务器安全等级
- 搭建 Nodejs 生产环境
- 配置 Nginx 实现反向代理
- 管理域名解析
- 配置 PM2 一键部署
- 发布上线
- 部署HTTPS
- Node 应用
- 爬虫(未完成)
- 例子
- 反爬虫
- 中间件
- body-parser
- connect-redis
- cookie-parser
- cors
- csurf
- express-session
- helmet
- ioredis
- log4js(未完成)
- uuid
- errorhandler
- nodeclub源码
- app.js
- config.js
- 消息队列
- RPC
- 性能优化
- 第七部分 总结
- Web服务器
- 目录结构
- 依赖
- 功能
- 代码片段
- 整理
- 知识清单、博客
- 项目、组件、库
- Node代码
- 面试必考
- 91算法
- 第八部分 工作代码总结
- 样式代码
- 框架代码
- 组件代码
- 功能代码
- 通用代码