[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算法
- 第八部分 工作代碼總結
- 樣式代碼
- 框架代碼
- 組件代碼
- 功能代碼
- 通用代碼