原文: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。

变换 x方向 y方向
缩放 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实现移动端图片预览:手势缩放, 手势拖动,双击放大...相关推荐

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

    查看示例效果: 一.功能介绍 图片预览主要有以下几个功能点组成:监听图片点击事件,进入图片预览模式 自定义手势事件, (双指缩放,滑动,双击...) 监听图片手势事件,通过 transform-mat ...

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

    查看示例效果: 一.功能介绍 图片预览主要有以下几个功能点组成:监听图片点击事件,进入图片预览模式 自定义手势事件, (双指缩放,滑动,双击...) 监听图片手势事件,通过 transform-mat ...

  3. 移动端图片预览插件-fly-zomm-img.min.js

    移动端图片预览插件,一个JQ的插件,支持手势放大缩小:有点小bug,不过感觉是可以接受的: 插件的地址:http://www.jq22.com/jquery-info15466 那里有具体的说明,但是 ...

  4. vue实现移动端图片预览

    vue实现移动端图片预览 1.安装插件 2.main引入 3.使用 注意 扩展 通过使用vue-photo-preview插件,实现移动端图片的预览,全屏等功能. 1.安装插件 npm install ...

  5. php 图片预览原理,JavaScript_纯JS实现的批量图片预览加载功能,1.实现原理直接见代码,需要一 - phpStudy...

    纯JS实现的批量图片预览加载功能 1.实现原理直接见代码,需要一张转圈的小图片,需要预览的所有图片默认的位置全是这张小图片,滚轮滚到原图需要出现的位置时候,预览加载替换小图片.实现效果 复制代码 代码 ...

  6. JS使用H5实现图片预览

    上传图片实现预览的功能,以下是代码的实现: <!DOCTYPE html> <html> <head> <meta charset="UTF-8&q ...

  7. vue移动端图片预览组件

    更新说明:1.02版本采用show属性值控制组件的显示与隐藏,不再使用v-if!! 安装:npm install -s w-previewimg 或 yarn add w-previewimg 在线预 ...

  8. pc 图片预览放大 端vue_移动端Vue.js的图片预览组件,支持放缩、滑动功能!

    功能:图片预览组件,支持双手指放大/缩小,双击放大/缩小,单击消失隐藏. 注:touch事件请手机预览 源码分享 组件参数 data() { return { loading: 2, // 1成功 2 ...

  9. html5 图片上传,支持图片预览、压缩、及进度显示,兼容IE6+及标准浏览器

    原文:html5 图片上传,支持图片预览.压缩.及进度显示,兼容IE6+及标准浏览器 以前写过上传组件,见 打造 html5 文件上传组件,实现进度显示及拖拽上传,兼容IE6+及其它标准浏览器,对付一 ...

最新文章

  1. datediff什么作用php,php中easter_date函数的功能起什么作用呢?
  2. 创业公司崛起靠加班?别闹了,早死才靠加班
  3. [译]Selenium Python文档:一、安装
  4. 关于 Head First SQL 中文版
  5. Web Service 基本概念和技术应用
  6. C# 使用 Index 和 Range 简化集合操作
  7. eclipse导出war包放入服务器中
  8. 20145202 《信息安全系统设计基础》第6周学习总结(2)
  9. php- osc,pt-osc使用一则
  10. JAVA学习笔记(七)细胞自动机(带注释)
  11. VC编程获取和修改环境变量,不重启系统即时生效
  12. Android复制assets文件到SD卡
  13. 【AI简报20210702期】骁龙888 plus发布、RISC-V处理器大飞跃
  14. php制作万年历的步骤_PHP制作万年历
  15. mysql注入时的注释处理
  16. 无法启动此程序因为计算机中丢失vcruntime140_1.dll
  17. linux中Swap分区是做什么的?
  18. 自动化测试需要学习的知识
  19. thunderbird重复的已发送
  20. 文章4片 观察者模式

热门文章

  1. java 爬虫 百度新闻_基于HttpClient实现网络爬虫~以百度新闻为例
  2. R语言包安装方法,及优质包推荐
  3. 流式细胞凋亡检测实验常见问题解析
  4. pycharm中无法安装scipy、imread、GDAL等库
  5. ab plc软件_回收拆机拆厂二手机械设备回收PLC自动化物资回收【研发吧】
  6. 安装 | VMware Workstation Pro 16 for Unbuntu 20.10 虚拟机安装教程
  7. C语言 | 基于STM32实现AT24CXX应用(代码类)
  8. 计算机组成原理第一阶段测试,计算机组成原理随堂测验1附答案
  9. 如何激发孩子的想象力_如何激发孩子的想象力?这4个方面要了解,让孩子快乐成长...
  10. 声学漫谈之五:音腔是怎么影响声音效果的