js实现移动端图片预览:手势缩放, 手势拖动,双击放大...
前言
本文將介紹如何通過js實現移動端圖片預覽,包括圖片的 預覽模式,手勢縮放,手勢拖動,雙擊放大等基本功能;
掃碼查看示例效果:
代碼地址http://pangyongsheng.github.io/imgPreview/
一、功能介紹
圖片預覽主要有以下幾個功能點組成:
- 監聽圖片點擊事件,進入圖片預覽模式
- 自定義手勢事件, (雙指縮放,滑動,雙擊。。。)
- 監聽圖片手勢事件,通過 transform-matrix 實現圖片的各種變換;
?
二、實現方法
1、圖片預覽模式
圖片預覽即點擊圖片在頁面中插入一個黑色全屏背景框并將圖片居中顯示。封裝時,為了只對指定圖片添加功能,可通過監聽指定類名或添加某種屬性的img標簽監聽;另外需在對背景框綁定點擊事件,退出預覽模式。一下是一個簡單示例代碼:
?//點擊圖片進入預覽????var?$Dom?=?document.querySelector(".preview");
????$Dom.onclick?=?function()?{
????????var?temp?=?this.src;
????????var?objE?=?document.createElement("div");
????????objE.innerHTML?=?'<div?class="bgM"?>'?+
????????????????'<img?src="'+temp+'"??id="img_scan"?class="img-custom-img2"/>'?+
????????????'</div>';
????????document.body.appendChild(objE.children[0]);
????????//退出圖片預覽事件
????????var?$bg?=?document.querySelector(".bgM");
????????$bg.onclick?=?function()?{
????????????var?dm?=?document.querySelector(".bgM");
????????????document.body.removeChild(dm);
????????}
????????//阻止事件冒泡
????????var?$img?=?document.querySelector(".img-custom-img2");
????????$img.onclick?=?function(event)?{
???????????event.stopPropagation();
????????}
????}
css樣式參考
.bgM{????width:?100%;
????height:?100%;
????position:?absolute;
????top:?0;left:?0;right:?0;bottom:?0;
????z-index:?1000;
????background-color:?rgba(0,0,0,0.85);
????overflow:?hidden;
}
.bgM?img{
????width:?100%;
????max-height:100%;
????position:?absolute;
????top:?0;left:?0;right:?0;bottom:?0;
????z-index:?1001;
????margin:?auto;
}
2、自定義手勢事件
這里通過監聽移動端touch事件實現自定義雙指縮放,單指滑動,雙擊事件,并通過事件屬性傳遞相關參數,如縮放比例,滑動距離等,詳細實現方式參考這篇博客:請參考此博文:https://www.cnblogs.com/pangys/p/9119845.html 這里只大概說明;
當觸發touch事件的時候,會生成一個TouchEvent對象,我們可通過其屬性e.touches.length來判斷是否多點觸控,通過e.touches[index].pageX,e.touches[index].pageY獲取去觸點坐標,通過e.target獲取dom節點;
這里為了方便,直接監聽document事件然后對目標元素觸發事件,實際也可以直接對img監聽事件,然后分別處理;
(1)手勢事件
- 監聽touchstart事件,若e.touches.length>=2,為雙指事件,獲取觸點坐標(觸點坐標-目標元素.offsetLeft/Top)計算兩個點中點 添加到事件屬性中,改變相關狀態,觸發gesturestart事件;
- 監聽touchmove事件,若e.touches.length>=2,獲當前取觸點坐標和gesturestart坐標,計算出縮放比例及角度,觸發gesturechange事件;
- 監聽touchend事件,根據前面事件記錄的狀態觸發結束gestureend事件;
(2)滑動事件
- 監聽touchstart事件,若e.touches.length<2,為單指事件,獲取觸點坐標(觸點坐標-目標元素.offsetLeft/Top)添加到事件屬性中,記錄事件狀態;
- 監聽touchmove事件,若e.touches.length<2,獲當前取觸點坐標和上一步坐標,計算出移動距離添加到事件屬性中,觸發swipeMove事件;
(3)雙擊事件
監聽touchstart事件,若e.touches.length<2,為單指事件,獲取觸點坐標(觸點坐標-目標元素.offsetLeft/Top)添加到事件屬性中,獲取當前時間挫記錄到相關變量中,計算本次時間戳與上次事件時間戳之差,若時間差范圍在指定范圍(0~250)則觸發doubleTouch事件;
參考代碼:
????var?isTouch?=?false;????var?isDoubleTouch?=?false;?//是否為多觸點???
????var?start?=?[];?//存放觸點坐標
????var?now,?delta;?//當前時間,兩次觸發事件時間差
????var?startPosition,?movePosition,?endPosition;?//滑動起點,移動,結束點坐標
????//事件聲明
????var?gesturestart?=?new?CustomEvent('gesturestart');
????var?gesturechange?=?new?CustomEvent('gesturechange');
????var?gestureend?=?new?CustomEvent('gestureend');
????var?swipeMove?=?new?CustomEvent('swipeMove');
????var?doubleTouch?=?new?CustomEvent("doubleTouch");
????//監聽touchstart事件
????document.addEventListener('touchstart',?function(e)?{
????????if?(e.touches.length?>=?2)?{?//判斷是否有兩個點在屏幕上
????????????isDoubleTouch?=?true;
????????????start?=?e.touches;?//得到第一組兩個點
????????????var?screenMinPoint?=?getMidpoint(start[0],?start[1]);?//獲取兩個觸點中心坐標
????????????gesturestart.midPoint?=?[screenMinPoint[0]?-?e.target.offsetLeft,?screenMinPoint[1]?-?e.target.offsetTop];?//獲取中心點坐標相對目標元素坐標
????????????e.target.dispatchEvent(gesturestart);
????????}?else?{
????????????delta?=?Date.now()?-?now;?//計算兩次點擊時間差
????????????now?=?Date.now();
????????????startPosition?=?[e.touches[0].pageX,?e.touches[0].pageY];
????????????if?(delta?>?0?&&?delta?<=?250)?{?//雙擊事件
????????????????doubleTouch.position?=?[e.touches[0].pageX?-?e.target.offsetLeft,?e.touches[0].pageY?-?e.target.offsetTop];
????????????????e.target.dispatchEvent(doubleTouch);
????????????}?else?{?//滑動事件
????????????????isTouch?=?true;
????????????}
????????}
????},?false);
????//監聽touchmove事件
????document.addEventListener('touchmove',?function(e)?{
????????if?(e.touches.length?>=?2?&&?isDoubleTouch)?{?//手勢事件
????????????var?now?=?e.touches;?//得到第二組兩個點
????????????var?scale?=?getDistance(now[0],?now[1])?/?getDistance(start[0],?start[1]);?//得到縮放比例
????????????var?rotation?=?getAngle(now[0],?now[1])?-?getAngle(start[0],?start[1]);?//得到旋轉角度差
????????????gesturechange.scale?=?scale.toFixed(2);
????????????gesturechange.rotation?=?rotation.toFixed(2);
????????????e.target.dispatchEvent(gesturechange);
????????}?else?if?(isTouch)?{
????????????movePosition?=?[e.touches[0].pageX,?e.touches[0].pageY];
????????????endPosition?=?movePosition;
????????????movePosition?=?[movePosition[0]?-?startPosition[0],?movePosition[1]?-?startPosition[1]];
????????????startPosition?=?[e.touches[0].pageX,?e.touches[0].pageY];
????????????swipeMove.distance?=[movePosition[0].toFixed(2)?,?movePosition[1].toFixed(2)];
????????????e.target.dispatchEvent(swipeMove);
????????}
????},?false);
????//監聽touchend事件
????document.addEventListener('touchend',?function(e)?{
????????if?(isDoubleTouch)?{
????????????isDoubleTouch?=?false;
????????????gestureend.position?=?endPosition;
????????????e.target.dispatchEvent(gestureend);
????????};
????},?false);
????/*
?????*?兩點的距離
?????*/
????function?getDistance(p1,?p2)?{
????????var?x?=?p2.pageX?-?p1.pageX,
????????????y?=?p2.pageY?-?p1.pageY;
????????return?Math.sqrt((x?*?x)?+?(y?*?y));
????};
????/*
?????*?兩點的夾角
?????*/
????function?getAngle(p1,?p2)?{
????????var?x?=?p1.pageX?-?p2.pageX,
????????????y?=?p1.pageY?-?p2.pageY;
????????return?Math.atan2(y,?x)?*?180?/?Math.PI;
????};
????/*
?????*?獲取中點?
?????*/
????function?getMidpoint(p1,?p2)?{
????????var?x?=?(p1.pageX?+?p2.pageX)?/?2,
????????????y?=?(p1.pageY?+?p2.pageY)?/?2;
????????return?[x,?y];
????}
三、圖片的變換
對于圖片的每次操作都需在上一次操作的基礎上進行疊加,如果直接使用width,top,left或scale,translate等css樣式需要每次都記錄當前圖片狀態的全部參數,而且計算較多,這里考慮使用transform-matrix實現圖片的基本變換,這樣只需創建一個數組作為變換矩陣,每次操作直接在當前變換矩陣上修改相關參數即可實現圖像的變換:
transform-matrix :可配置[a,b,c,d,e,f]6個參數,如下圖所示,x和y是初始的坐標,x’ 和y’則是通過矩陣變換后得到新的坐標。變換矩陣,對原先的坐標施加變換,就能得到新的坐標了。依據矩陣變換規則即可得到: x’=ax+cy+e y’=bx+dy+f。
| 縮放 | a | d |
| 移動 | e | f |
(1) 獲取目標元素及相關參數,綁定事件
????var?$imgs?=?document.querySelector("#img_scan");????var?clientWidth?=?document.body.clientWidth;?//窗口寬
????var?clientHeight?=?document.body.clientHeight;?//窗口高
????var?imgWidth?=?parseInt(window.getComputedStyle($imgs).width);?//圖片寬
????var?imgHeight?=?parseInt(window.getComputedStyle($imgs).height);?//圖片高
????$imgs.addEventListener('gesturestart',?gesturef,?false);
????$imgs.addEventListener('gesturechange',?gesturef,?false);
????$imgs.addEventListener('gestureend',?gesturef,?false);
????$imgs.addEventListener('swipeMove',?gesturef,?false);
????$imgs.addEventListener('doubleTouch',?gesturef,?false);
????var?tMatrix?=?[1,?0,?0,?1,?0,?0];?//x縮放,無,無,y縮放,x平移,y平移
????var?originLast,?maxSwipeLeft,?maxSwipeRight,?maxSwipeTop,?maxSwipeBottom;?//上下左右可拖動距離
(2)監聽 gesturestart 設置 變換中心
?case?"gesturestart":????var?x?=?event.midPoint[0];
????var?y?=?event.midPoint[1];
????originLast?=?event.midPoint;
????$imgs.style.transformOrigin?=?x?+?"px?"?+?y?+?"px";
????break;
(2)監聽 gesturechange 進行縮放變換,這里設置了縮放范圍為0.5 ~ 3;
case?"gesturechange":?????var?sc?=?parseFloat(event.scale);
?????tMatrix[0]?=?tMatrix[0]?+?sc?-?1?>?0.5?&&?tMatrix[0]?+?sc?-?1?<?3???tMatrix[0]?+?sc?-?1?:?tMatrix[0];
?????tMatrix[3]?=?tMatrix[3]?+?sc?-?1?>?0.5?&&?tMatrix[3]?+?sc?-?1?<?3???tMatrix[3]?+?sc?-?1?:?tMatrix[3];
?????var?temp?=?tMatrix.join(",");
?????$imgs.style.transform?=?"matrix("?+?temp?+?")";
?????break;
(3)監聽 gestureend 獲取移動邊界范圍邊界
case?"gestureend":?????maxMove();
?????break;
可移動邊界范圍的計算:
對于圖片中的任意點可拖動范圍都是相同的,那么以縮放中心點來計算,如下圖所示,對于圖片中的縮放中心點p,有縮放后距離邊距的距離,可移動的范圍均為 縮放后增加或減少的距離 - (縮放中心點距離圖片邊緣的距離),即 |?縮放比例 - 1 |? *? p點距離邊緣的距離;
?
代碼如下:
?function?maxMove(){?????//最大可拖動范圍
?????var?sca?=?tMatrix[0];
?????maxSwipeLeft?=?Math.abs(sca?-?1)?*?originLast[0];
?????maxSwipeRight?=?Math.abs(sca?-?1)?*?(imgWidth?-?originLast[0]);
?????maxSwipeTop?=?Math.abs(sca?-?1)?*?originLast[1];
?????maxSwipeBottom?=?Math.abs(sca?-?1)?*?(imgHeight?-?originLast[1]);
}
(4)監聽 swipeMove 拖動圖片,需考慮是否在可拖動范圍
?if?(!maxSwipeLeft?||?!maxSwipeRight?||?!maxSwipeTop?||?!maxSwipeBottom)?return;?????if?(event.distance[0]?>?0?&&?maxSwipeLeft?<?tMatrix[4])?return;
?????if?(event.distance[0]?<?0?&&?maxSwipeRight?<?-tMatrix[4])?return;
?????if?(event.distance[1]?>?0?&&?maxSwipeTop?<?tMatrix[5])?return;
?????if?(event.distance[1]?<?0?&&?maxSwipeBottom?<?-tMatrix[5])?return;
?????tMatrix[4]?=?tMatrix[4]?+?parseInt(event.distance[0]);
?????tMatrix[5]?=?tMatrix[5]?+?parseInt(event.distance[1]);
?????var?temp?=?tMatrix.join(",");
?????$imgs.style.transform?=?"matrix("?+?temp?+?")";
?????break;
(5)監聽 doubleTouch 實現雙擊點縮放
case?"doubleTouch":??????originLast?=?event.position;
??????$imgs.style.transformOrigin?=?event.position[0]?+?"px?"?+?event.position[1]?+?"px";
??????tMatrix[0]?=?2;
??????tMatrix[3]?=?2;
??????var?temp?=?tMatrix.join(",");
??????$imgs.style.transform?=?"matrix("?+?temp?+?")";
??????maxMove();
??????break;
至此一個圖片預覽的基本功能即可實現 ,? 也可以通過手勢做旋轉及上下一張的功能;
總結
以上是生活随笔為你收集整理的js实现移动端图片预览:手势缩放, 手势拖动,双击放大...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 精读《谈谈 Web Workers》
- 下一篇: Redis(七)分布式锁