移動開發之手勢與雙指縮放
上周遇見一個關于雙指縮放的問題,同時這個雙指縮放也比較常見,于是決定對移動開發手勢做一個學習和總結,并給出一個雙指縮放的實例,希望對讀者提供一些幫助。
先給一個例子,點此查看雙指縮放實例
前言
當年喬布斯的iphone第一次支持多點觸控時,確實驚艷了世人,而現在大部分手機都支持多點觸控,這就有了手勢這個概念,通過多點觸控,形成不同的手勢,開發者根據不同手勢,提供不同功能,比如雙指縮放,是很常見的。
ios的多點觸摸
ios的Safari瀏覽器是第一個支持多點觸摸的瀏覽器,并提供觸摸API供開發者使用,到后來Android 3也開始支持多點觸摸,各大瀏覽器也借鑒Safari提出了觸摸API,除了個別硬件支持屬性外,大致相同,這是值得慶幸的。
IE10的多點觸摸
雖然現在市場上WP手機越來越少,但是為了更完整的學習理解手勢知識,我們也需要進行學習。IE10支持多點觸摸,但是其多點觸摸與ios和android不同,ios和android瀏覽器為多點觸摸提供一個包含touches數組的事件,包含所有多點觸摸對象,而IE10為多點觸摸的每一個觸摸點創建一個單獨的觸摸事件。
漸進增強與手勢
我們知道手勢很方便,用戶也很喜歡手勢,但是我們也要明白手勢并不總是能使用,有些設備或瀏覽器還是不支持的,所以我們最合適的是基于普通點擊和觸摸事件交互的頁面,將手勢作為增強的交互,這就是通常所說的漸進增強思想。
觸摸事件
既然是要漸進增強,那就必須從基礎點擊和觸摸事件說起,點擊事件就不再多說,關于觸摸事件,之前也有一篇文章介紹過 移動開發之輕觸與單擊事件 ,關于觸摸四種基礎事件和事件對象,可以查看該文章,這里就不重復了,這里要對觸摸事件的處理進行說明。
瀏覽器兼容
通常我們在觸摸和手勢事件處理函數內會添加CSS3動畫處理,這個時候需要進行瀏覽器兼容處理。
var TRANSITION = 'transition';
var TRANSITION_END = 'transitionend';
var TRANSFORM = 'transform';
var TRANSFORM_PROPERTY = 'transform';
var TRANSITION_PROPERTY = 'transition';
if (typeof document.body.style.webkitTransform !== undefined) {
TRANSFORM = 'webkitTransform';
TRANSITION = 'webkitTransition';
TRANSITION_END = 'webkitTransitionEnd';
TRANSFORM_PROPERTY = '-webkit-transform';
TRANSITION_PROPERTY = '-webkit-transition';
}
雙指縮放
在移動端手勢事件中,雙指縮放的需求還是很常見的,本節詳細闡述,現在大多數移動設備都支持原生的雙指縮放,但是這樣可能影響頁面布局,而且為了更多可控性,很多時候還是會選擇自行實現縮放手勢功能。
縮放中心
在原生縮放動畫,縮放中心通常是圖像中心,可以拿起移動設備嘗試一番,而在現實需求中,我們通常希望縮放以手勢屏幕的兩個接觸點中心為中心,即縮放中心為接觸點中心。
變換原點
在CSS3動畫中,如, transform動畫旋轉,縮放等動畫 ,有一個 transform-origin 屬性:
The transform-origin property lets you modify the origin for transformations of an element.
transform-origin 屬性使得我們可以修改一個元素變換動畫的原點。
當我們使用CSS3變換動畫進行縮放時,由于變換動畫的變換原點和縮放中心(即接觸點中心)相互獨立,為了不影響縮放中心相對于元素的位置,元素變換縮放時,也應該對縮放中心坐標進行縮放移動,即除了變換縮放元素,還應該對該元素進行額外坐標偏移。
位移偏量
我們希望縮放以接觸點中心為中心,元素向四周縮放,假設變換原點為元素左上角,即 transform-origin: 0 0; ,此時元素變換縮放位移均為正值,僅向右下方向縮放,所以縮放中心(其實也就是該元素)應對應向相反方向(左上方向)偏移,值為 縮放坐標值 * (1 - 縮放比例) 。
如上圖,原始元素A盒,以接觸點M,N兩點的中心點L(x, y)為縮放中心點,向四周縮放,其位置應該如圖中C盒,由于L(x, y)點的位置是不可控的(用戶行為),我們設置元素變換原點 transform-origin: 0 0; ,此時其縮放后位置如B盒,然而我們依然希望達到縮放后形成C盒的位移效果,則我們需要將元素向左上方向位移,假設縮放比例為k, 則元素位移值計算過程如下:
- 1.計算元素A的位移,其實等效于縮放點的位移,也就是計算坐標系縮放后進行的位移;
- 2. L(x, y) 點,在縮放前坐標系中坐標為 (x, y) ,則縮放k倍后,其在縮放后坐標系中坐標為 (kx, ky) ;
- 3.計算兩點位移距離:
- 水平位移: x – kx;
- 垂直位移:y – ky;
- 4.得到元素在水平和垂直方向需要偏移的距離。
縮放的實現
縮放與滾動
我們自行實現的縮放事件需要使用 touchmove 事件,而 touchmove 事件會觸發設備的滾動事件,所以要么阻止滾動,要么不讓屏幕出現滾動條。
-
阻止滾動
document.addEventListener('touchmove', function(event){ event.preventDefault(); })
-
阻止滾動條
html, body { height: 100%; }
計算縮放中心
縮放動畫以接觸點為中心,當我們使用雙指手勢實現縮放時,這個接觸點中心就是兩個接觸點的中心點:
function getOrigin(first, second) {
return {
x: (first.x + second.x) / 2,
y: (first.y + second.y) / 2
};
}
getOrigin({
x: event.touches[0].pageX,
y: event.touches[0].pageY
}, {
x: event.touches[1].pageX,
y: event.touches[1].pageY
});
計算縮放比例
縮放比例如何確定呢,起始觸摸兩指間距離除以縮放時兩指間距離,即縮放比例:
function getDistance(start, stop) {
return Math.sqrt(Math.pow((stop.x - start.x), 2) + Math.pow((stop.y - start.y), 2));
}
function getScale(start, stop) {
return getDistance(start[0], start[1]) / getDistance(stop[0], stop[1]);
}
處理觸摸事件
要實現縮放功能除了計算相關縮放中心和縮放比例,另外還需要處理觸摸事件:
var distance = {};
var origin;
var scale = 1;
function handleTouch(e) {
switch(e.type) {
case 'touchstart':
if (e.touches.length > 1) {
distance.start = getDistance({
x: e.touches[0].screenX,
y: e.touches[0].screenY
}, {
x: e.touches[1].screenX,
y: e.touches[1].screenY
});
}
break;
case 'touchmove':
if (e.touches.length === 2) {
origin = getOrigin({
x: event.touches[0].pageX,
y: event.touches[0].pageY
}, {
x: event.touches[1].pageX,
y: event.touches[1].pageY
});
distance.stop = getDistance({
x: e.touches[0].screenX,
y: e.touches[0].screenY
}, {
x: e.touches[1].screenX,
y: e.touches[1].screenY
});
scale = distance.stop / distance.start;
setScaleAnimation(scale, true);
}
break;
case 'touchend':
scale = 1;
setScaleAnimation(scale);
break;
case 'touchcancel':
scale = 1;
setScaleAnimation(scale);
break;
default:;
}
}
使用變換
接下來就是使用CSS3動畫縮放圖片了,代碼如下:
function setScaleAnimation(scale, animation) {
var transition_animation = '';
var x, y;
if (animation) {
transition_animation = 'none';
} else {
transition_animation = TRANSFORM_PROPERTY + ' 0.3s ease-out';
}
element.style[TRANSITION] = transition_animation;
// 計算位移偏量
x = origin.x + (-origin.x) * scale;// 縮放中心偏移量
y = origin.y + (-origin.y) * scale;
// 縮放和位移
element.style[TRANSFORM] = 'matrix(' + scale + ', 0, 0, ' + scale + ', ' + x + ', ' + y + ')';
}
本文關于移動開發手勢及實現雙指縮放介紹到此完結,實現的實例比較粗糙,但原理基本闡述清楚,感興趣可以自己動手實現,優化;本文很久以前就開篇了,但直到今天才終于完筆,若有不足,也歡迎指正。
來自:http://blog.codingplayboy.com/2017/04/16/mobile_gesture/