js实现移动端图片预览:手势缩放, 手势拖动,双击放大...

摘要:
)监控图像手势事件,通过变换矩阵实现各种图像变换;2、 实现方法1、图片预览模式:点击图片在页面中插入一个黑色的全屏背景框,并在中间显示图片。在封装期间,为了只向指定的图像添加函数,可以侦听指定类名或属性的img标记;此外,您需要将单击事件绑定到背景框以退出预览模式。

前言
本文将介绍如何通过js实现移动端图片预览,包括图片的 预览模式,手势缩放,手势拖动,双击放大,单击退出基本功能;

扫码查看示例效果:
js实现移动端图片预览:手势缩放, 手势拖动,双击放大...第1张
代码地址http://pangyongsheng.github.io/imgPreview/

一、功能介绍

图片预览主要有以下几个功能点组成:

  • 监听图片点击事件,进入图片预览模式
  • 自定义手势事件, (双指缩放,滑动,双击。。。)
  • 监听图片手势事件,通过 transform-matrix 实现图片的各种变换;

二、实现方法

1、图片预览模式

图片预览即点击图片在页面中插入一个黑色全屏背景框并将图片居中显示。封装时,为了只对指定图片添加功能,可通过监听指定类名或添加某种属性的img标签监听;另外需在对背景框绑定点击事件,退出预览模式。一下是一个简单示例代码:

//点击图片进入预览
var$Dom=document.querySelector(".preview");
$Dom.onclick=function(){
vartemp=this.src;
varobjE=document.createElement("div");
objE.innerHTML='<divclass="bgM">'+
'<imgsrc="'+temp+'"id="img_scan" />'+
'</div>';
document.body.appendChild(objE.children[0]);
//退出图片预览事件
var$bg=document.querySelector(".bgM");
$bg.onclick=function(){
vardm=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;
}
.bgMimg{
width: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事件;

参考代码:

varisTouch=false;
varisDoubleTouch=false;//是否为多触点
varstart=[];//存放触点坐标
var timer = null ;
varnow,delta;//当前时间,两次触发事件时间差
varstartPosition,movePosition,endPosition;//滑动起点,移动,结束点坐标
//事件声明
vargesturestart=newCustomEvent('gesturestart');
vargesturechange=newCustomEvent('gesturechange');
vargestureend=newCustomEvent('gestureend');
varswipeMove=newCustomEvent('swipeMove');
vardoubleTouch=newCustomEvent("doubleTouch");
//监听touchstart事件
document.addEventListener('touchstart',function(e){
if(e.touches.length>=2){//判断是否有两个点在屏幕上
isDoubleTouch=true;
start=e.touches;//得到第一组两个点
varscreenMinPoint=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){//双击事件
clearTimeout(timer);

doubleTouch.position=[e.touches[0].pageX-e.target.offsetLeft,e.touches[0].pageY-e.target.offsetTop];
e.target.dispatchEvent(doubleTouch);
}else{//滑动事件
timer = setTimeout(function(){ //单击事件
e.target.dispatchEvent(oneTouch);
})
}
isTouch=true;
}
},false);
//监听touchmove事件
document.addEventListener('touchmove',function(e){
clearTimeout(timer);
if(e.touches.length>=2&&isDoubleTouch){//手势事件
varnow=e.touches;//得到第二组两个点
varscale=getDistance(now[0],now[1])/getDistance(start[0],start[1]);//得到缩放比例
varrotation=getAngle(now[0],now[1])-getAngle(start[0],start[1]);//得到旋转角度差
gesturechange.scale=scale.toFixed(2);
gesturechange.rotation=rotation.toFixed(2);
e.target.dispatchEvent(gesturechange);
}elseif(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);
/*
*两点的距离
*/

functiongetDistance(p1,p2){
varx=p2.pageX-p1.pageX,
y=p2.pageY-p1.pageY;
returnMath.sqrt((x*x)+(y*y));
};
/*
*两点的夹角
*/

functiongetAngle(p1,p2){
varx=p1.pageX-p2.pageX,
y=p1.pageY-p2.pageY;
returnMath.atan2(y,x)*180/Math.PI;
};
/*
*获取中点
*/

functiongetMidpoint(p1,p2){
varx=(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。

js实现移动端图片预览:手势缩放, 手势拖动,双击放大...第2张

变换x方向y方向
缩放ad
移动ef

(1) 获取目标元素及相关参数,绑定事件

var$imgs=document.querySelector("#img_scan");
varclientWidth=document.body.clientWidth;//窗口宽
varclientHeight=document.body.clientHeight;//窗口高
varimgWidth=parseInt(window.getComputedStyle($imgs).width);//图片宽
varimgHeight=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);
$imgs.addEventListener('oneTouch',gesturef,false);
vartMatrix=[1,0,0,1,0,0];//x缩放,无,无,y缩放,x平移,y平移
varoriginLast,maxSwipeLeft,maxSwipeRight,maxSwipeTop,maxSwipeBottom;//上下左右可拖动距离

(2)监听 gesturestart 设置 变换中心

case"gesturestart":
varx=event.midPoint[0];
vary=event.midPoint[1];
originLast=event.midPoint;
$imgs.style.transformOrigin=x+"px"+y+"px";
break;

(2)监听 gesturechange 进行缩放变换,这里设置了缩放范围为0.5 ~ 3;

case"gesturechange":
varsc=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];
vartemp=tMatrix.join(",");
$imgs.style.transform="matrix("+temp+")";
break;

(3)监听 gestureend 获取移动边界范围边界

case"gestureend":
maxMove();
break;

可移动边界范围的计算:

对于图片中的任意点可拖动范围都是相同的,那么以缩放中心点来计算,如下图所示,对于图片中的缩放中心点p,有缩放后距离边距的距离,可移动的范围均为 缩放后增加或减少的距离 - (缩放中心点距离图片边缘的距离),即 |缩放比例 - 1 | * p点距离边缘的距离;

js实现移动端图片预览:手势缩放, 手势拖动,双击放大...第3张

代码如下:

functionmaxMove(){
//最大可拖动范围
varsca=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]);
vartemp=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;
vartemp=tMatrix.join(",");
$imgs.style.transform="matrix("+temp+")";
maxMove();
break;

(6)监听 oneTouch退出预览

case "oneTouch":
  var $bg = document.querySelector(".bgM");
  document.body.removeChild($bg);
  break;
至此一个图片预览的基本功能即可实现 , 也可以通过手势做旋转及上下一张的功能;

免责声明:文章转载自《js实现移动端图片预览:手势缩放, 手势拖动,双击放大...》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Nmap简单扫描Windows识别USB设备过程下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

模板引擎doT.js用法详解(HTML模板引擎)

作为一名前端攻城师,经常会遇到从后台ajax拉取数据再显示在页面的情境,一开始我们都是从后台拉取再用字符串拼接的方式去更达到数据显示在页面! <!-- 显示区域 --> <div id="testid"></div> <script type="text/javascript"> var testjso...

js遍历table中的数据,并组装成json

1 functionfore(){ 2 var temp = ""; 3 var tabLen = document.getElementById("tableID"); 4 var jsonT = "{pieTes:["; 5 for(var i = 0; i < tabLen.row...

【Python3爬虫】一次应对JS反调试的记录

一、前言简介 在前面已经写过关于 JS 反调试的博客了,地址为:https://www.cnblogs.com/TM0831/p/12154815.html。但这次碰到的网站就不一样了,这个网站并不是通过不断调试消耗内存以反调试的,而是直接将页面替换修改掉,让人无法调试页面。 二、网页分析 本次爬取的网址为:https://www.aqistudy.cn...

JS全局添加token

全局添加token var token = sessionStorage.getItem("UserTocken"); if(token){ $.ajaxSetup({ //发送请求前触发 beforeSend: function(xhr) { //可以设置自定义标头 xhr....

原生JS实现双向链表

1.前言 双向链表和单向链表的区别在于,在链表中,一个节点只有链向下一个节点的链接,而在双向链表中,链接是双向的:一个链向下一个元素,另一个链向前一个元素,如下图所示: 从图中可以看到,双向链表中,在每个节点Node里有prev属性(指向上一个节点的指针)和next属性(指向下一个节点的指针),并且在链表中也有head属性(用来存储链表第一项的引用)和ta...

js获取移动端设备信息(IMEM,IMIS,手机型号,系统版本,浏览器信息等)

方法一: HTML+  封装好的方法,额外配置,使用指定方法打包才可用 属性: imei: 设备的国际移动设备身份码 imsi: 设备的国际移动用户识别码 model: 设备的型号 vendor: 设备的生产厂商 uuid: 设备的唯一标识 参考地址: http://www.html5plus.org/doc/zh_cn/device.htm...