## 一、觸摸操作概述
瀏覽器的觸摸 API 由三個部分組成。
* Touch:一個觸摸點
* TouchList:多個觸摸點的集合
* TouchEvent:觸摸引發的事件實例
`Touch`接口的實例對象用來表示觸摸點(一根手指或者一根觸摸筆),包括位置、大小、形狀、壓力、目標元素等屬性。有時,觸摸動作由多個觸摸點(多根手指)組成,多個觸摸點的集合由`TouchList`接口的實例對象表示。`TouchEvent`接口的實例對象代表由觸摸引發的事件,只有觸摸屏才會引發這一類事件。
很多時候,觸摸事件和鼠標事件同時觸發,即使這個時候并沒有用到鼠標。這是為了讓那些只定義鼠標事件、沒有定義觸摸事件的代碼,在觸摸屏的情況下仍然能用。如果想避免這種情況,可以用`event.preventDefault`方法阻止發出鼠標事件。
## 二、Touch 接口
### 2.1 Touch 接口概述
Touch 接口代表單個觸摸點。觸摸點可能是一根手指,也可能是一根觸摸筆。
瀏覽器原生提供`Touch`構造函數,用來生成`Touch`實例。
~~~
var touch = new Touch(touchOptions);
~~~
`Touch`構造函數接受一個配置對象作為參數,它有以下屬性。
* `identifier`:必需,類型為整數,表示觸摸點的唯一 ID。
* `target`:必需,類型為元素節點,表示觸摸點開始時所在的網頁元素。
* `clientX`:可選,類型為數值,表示觸摸點相對于瀏覽器窗口左上角的水平距離,默認為0。
* `clientY`:可選,類型為數值,表示觸摸點相對于瀏覽器窗口左上角的垂直距離,默認為0。
* `screenX`:可選,類型為數值,表示觸摸點相對于屏幕左上角的水平距離,默認為0。
* `screenY`:可選,類型為數值,表示觸摸點相對于屏幕左上角的垂直距離,默認為0。
* `pageX`:可選,類型為數值,表示觸摸點相對于網頁左上角的水平位置(即包括頁面的滾動距離),默認為0。
* `pageY`:可選,類型為數值,表示觸摸點相對于網頁左上角的垂直位置(即包括頁面的滾動距離),默認為0。
* `radiusX`:可選,類型為數值,表示觸摸點周圍受到影響的橢圓范圍的 X 軸半徑,默認為0。
* `radiusY`:可選:類型為數值,表示觸摸點周圍受到影響的橢圓范圍的 Y 軸半徑,默認為0。
* `rotationAngle`:可選,類型為數值,表示觸摸區域的橢圓的旋轉角度,單位為度數,在0到90度之間,默認值為0。
* `force`:可選,類型為數值,范圍在`0`到`1`之間,表示觸摸壓力。`0`代表沒有壓力,`1`代表硬件所能識別的最大壓力,默認為`0`。
### 2.2 Touch 接口的實例屬性
**(1)Touch.identifier**
`Touch.identifier`屬性返回一個整數,表示觸摸點的唯一 ID。這個值在整個觸摸過程保持不變,直到觸摸事件結束。
~~~
someElement.addEventListener('touchmove', function (e) {
for (var i = 0; i < e.changedTouches.length; i++) {
console.log(e.changedTouches[i].identifier);
}
}, false);
~~~
**(2)Touch.screenX,Touch.screenY,Touch.clientX,Touch.clientY,pageX,pageY**
`Touch.screenX`屬性和`Touch.screenY`屬性,分別表示觸摸點相對于屏幕左上角的橫坐標和縱坐標,與頁面是否滾動無關。
`Touch.clientX`屬性和`Touch.clientY`屬性,分別表示觸摸點相對于瀏覽器視口左上角的橫坐標和縱坐標,與頁面是否滾動無關。
`Touch.pageX`屬性和`Touch.pageY`屬性,分別表示觸摸點相對于當前頁面左上角的橫坐標和縱坐標,包含了頁面滾動帶來的位移。
**(3)Touch.radiusX,Touch.radiusY,Touch.rotationAngle**
`Touch.radiusX`屬性和`Touch.radiusY`屬性,分別返回觸摸點周圍受到影響的橢圓范圍的 X 軸半徑和 Y 軸半徑,單位為像素。乘以 2 就可以得到觸摸范圍的寬度和高度。
`Touch.rotationAngle`屬性表示觸摸區域的橢圓的旋轉角度,單位為度數,在`0`到`90`度之間。
上面這三個屬性共同定義了用戶與屏幕接觸的區域,對于描述手指這一類非精確的觸摸,很有幫助。指尖接觸屏幕,觸摸范圍會形成一個橢圓,這三個屬性就用來描述這個橢圓區域。
下面是一個示例。
~~~
div.addEventListener('touchstart', rotate);
div.addEventListener('touchmove', rotate);
div.addEventListener('touchend', rotate);
function rotate(e) {
var touch = e.changedTouches.item(0);
e.preventDefault();
src.style.width = touch.radiusX * 2 + 'px';
src.style.height = touch.radiusY * 2 + 'px';
src.style.transform = 'rotate(' + touch.rotationAngle + 'deg)';
};
~~~
**(4)Touch.force**
`Touch.force`屬性返回一個`0`到`1`之間的數值,表示觸摸壓力。`0`代表沒有壓力,`1`代表硬件所能識別的最大壓力。
**(5)Touch.target**
`Touch.target`屬性返回一個元素節點,代表觸摸發生時所在的那個元素節點。即使觸摸點已經離開了這個節點,該屬性依然不變。
## 三、TouchList 接口
`TouchList`接口表示一組觸摸點的集合。它的實例是一個類似數組的對象,成員是`Touch`的實例對象,表示所有觸摸點。用戶用三根手指觸摸,產生的`TouchList`實例就會包含三個成員,每根手指的觸摸點對應一個`Touch`實例對象。
它的實例主要通過觸摸事件的`TouchEvent.touches`、`TouchEvent.changedTouches`、`TouchEvent.targetTouches`這幾個屬性獲取。
它的實例屬性和實例方法只有兩個。
* `TouchList.length`:數值,表示成員數量(即觸摸點的數量)。
* `TouchList.item()`:返回指定位置的成員,它的參數是該成員的位置編號(從零開始)。
## 四、TouchEvent 接口
### 4.1 概述
TouchEvent 接口繼承了 Event 接口,表示由觸摸引發的事件實例,通常來自觸摸屏或軌跡板。除了被繼承的屬性以外,它還有一些自己的屬性。
瀏覽器原生提供`TouchEvent()`構造函數,用來生成觸摸事件的實例。
~~~
new TouchEvent(type, options)
~~~
`TouchEvent()`構造函數可以接受兩個參數,第一個參數是字符串,表示事件類型;第二個參數是事件的配置對象,該參數是可選的,對象的所有屬性也是可選的。除了`Event`接口的配置屬性,該接口還有一些自己的配置屬性。
* `touches`:`TouchList`實例,代表所有的當前處于活躍狀態的觸摸點,默認值是一個空數組`[]`。
* `targetTouches`:`TouchList`實例,代表所有處在觸摸的目標元素節點內部、且仍然處于活動狀態的觸摸點,默認值是一個空數組`[]`。
* `changedTouches`:`TouchList`實例,代表本次觸摸事件的相關觸摸點,默認值是一個空數組`[]`。
* `ctrlKey`:布爾值,表示 Ctrl 鍵是否同時按下,默認值為`false`。
* `shiftKey`:布爾值,表示 Shift 鍵是否同時按下,默認值為`false`。
* `altKey`:布爾值,表示 Alt 鍵是否同時按下,默認值為`false`。
* `metaKey`:布爾值,表示 Meta 鍵(或 Windows 鍵)是否同時按下,默認值為`false`。
### 4.2 實例屬性
TouchEvent 接口的實例具有`Event`實例的所有屬性和方法,此外還有一些它自己的實例屬性,這些屬性全部都是只讀。
**(1)TouchEvent.altKey,TouchEvent.ctrlKey,TouchEvent.shiftKey,TouchEvent.metaKey**
* `TouchEvent.altKey`:布爾值,表示觸摸時是否按下了 Alt 鍵。
* `TouchEvent.ctrlKey`:布爾值,表示觸摸時是否按下了 Ctrl 鍵。
* `TouchEvent.shiftKey`:布爾值:表示觸摸時是否按下了 Shift 鍵。
* `TouchEvent.metaKey`:布爾值,表示觸摸時是否按下了 Meta 鍵(或 Windows 鍵)。
下面是一個示例。
~~~
someElement.addEventListener('touchstart', function (e) {
console.log('altKey = ' + e.altKey);
console.log('ctrlKey = ' + e.ctrlKey);
console.log('metaKey = ' + e.metaKey);
console.log('shiftKey = ' + e.shiftKey);
}, false);
~~~
**(2)TouchEvent.changedTouches**
`TouchEvent.changedTouches`屬性返回一個`TouchList`實例,成員是一組`Touch`實例對象,表示本次觸摸事件的相關觸摸點。
對于不同的時間,該屬性的含義有所不同。
* `touchstart`事件:被激活的觸摸點
* `touchmove`事件:發生變化的觸摸點
* `touchend`事件:消失的觸摸點(即不再被觸碰的點)
下面是一個示例。
~~~
someElement.addEventListener('touchmove', function (e) {
for (var i = 0; i < e.changedTouches.length; i++) {
console.log(e.changedTouches[i].identifier);
}
}, false);
~~~
**(3)TouchEvent.touches**
`TouchEvent.touches`屬性返回一個`TouchList`實例,成員是所有仍然處于活動狀態(即觸摸中)的觸摸點。一般來說,一個手指就是一個觸摸點。
下面是一個示例。
~~~
someElement.addEventListener('touchstart', function (e) {
switch (e.touches.length) {
// 一根手指觸摸
case 1: handle_one_touch(e); break;
// 兩根手指觸摸
case 2: handle_two_touches(e); break;
// 三根手指觸摸
case 3: handle_three_touches(e); break;
// 其他情況
default: console.log('Not supported'); break;
}
}, false);
~~~
**(4)TouchEvent.targetTouches**
`TouchEvent.targetTouches`屬性返回一個`TouchList`實例,成員是觸摸事件的目標元素節點內部、所有仍然處于活動狀態(即觸摸中)的觸摸點。
~~~
function touches_in_target(ev) {
return (ev.touches.length === ev.targetTouches.length ? true : false);
}
~~~
上面代碼用來判斷,是否所有觸摸點都在目標元素內。
## 五、觸摸事件的種類
觸摸引發的事件,有以下幾種。可以通過`TouchEvent.type`屬性,查看到底發生的是哪一種事件。
* `touchstart`:用戶開始觸摸時觸發,它的`target`屬性返回發生觸摸的元素節點。
* `touchend`:用戶不再接觸觸摸屏時(或者移出屏幕邊緣時)觸發,它的`target`屬性與`touchstart`事件一致的,就是開始觸摸時所在的元素節點。它的`changedTouches`屬性返回一個`TouchList`實例,包含所有不再觸摸的觸摸點(即`Touch`實例對象)。
* `touchmove`:用戶移動觸摸點時觸發,它的`target`屬性與`touchstart`事件一致。如果觸摸的半徑、角度、力度發生變化,也會觸發該事件。
* `touchcancel`:觸摸點取消時觸發,比如在觸摸區域跳出一個模態窗口(modal window)、觸摸點離開了文檔區域(進入瀏覽器菜單欄)、用戶的觸摸點太多,超過了支持的上限(自動取消早先的觸摸點)。
下面是一個例子。
~~~
var el = document.getElementsByTagName('canvas')[0];
el.addEventListener('touchstart', handleStart, false);
el.addEventListener('touchmove', handleMove, false);
function handleStart(evt) {
evt.preventDefault();
var touches = evt.changedTouches;
for (var i = 0; i < touches.length; i++) {
console.log(touches[i].pageX, touches[i].pageY);
}
}
function handleMove(evt) {
evt.preventDefault();
var touches = evt.changedTouches;
for (var i = 0; i < touches.length; i++) {
var touch = touches[i];
console.log(touch.pageX, touch.pageY);
}
}
~~~
- 第一章:移動開發入門
- 第一節:概述
- 第二節:基礎概念
- 第一課時:像素
- 第二課時:視口
- 第二章:Flex 布局
- 第一節:概述
- 第二節:容器屬性
- 第一課時:flex-direction 屬性
- 第二課時:flex-wrap 屬性
- 第三課時:flex-flow 屬性
- 第四課時:justify-content 屬性
- 第五課時:align-items 屬性
- 第六課時:align-content 屬性
- 第三節:項目屬性
- 第一課時:order 屬性
- 第二課時:flex-grow 屬性
- 第三課時:flex-shrink 屬性
- 第四課時:flex-basis 屬性
- 第五課時:flex 屬性
- 第六課時:align-self 屬性
- 第四節:Flex 實例
- 第一課時:常見頁面布局
- 第三章:響應式布局
- 第一節:概述
- 第二節:媒體查詢
- 第一課時:概述
- 第二課時:響應式設計
- 第三節:柵格系統
- 第一課時:概述
- 第二課時:案例分析
- 第三課時:Bootstrap 簡介
- 第四節:響應式案例
- 第一課時:三星首頁
- 第四章:移動端適配
- 第五章:移動端事件
- 第一節:概述
- 第二節:touch 事件
- 第三節:觸摸事件對象
- 第四節:其他事件
- 第五節:移動端幻燈片
- 第六章:移動端常見問題
- 第一節:瀏覽器兼容性
- 第二節:移動端動畫
- 第三節:300ms 延遲
- 第四節:文字溢出省略
- 第五節:水平居中和垂直居中
- 第七章:項目案例
- 第一節:美團外賣
- 第一課時:首頁
- 第二課時:訂單頁面
- 第三課時:我的頁面
- 第四課時:詳情頁面
- 第五課時:購物車頁