js图片查看器 - 灵感来自于picasa

本功能是为了解决运营对后台管理系统中用户上传的各种角度和尺寸的图片难以浏览的问题,于是花了两天时间写了这个插件,给大家分享。

兼容现代浏览器,不兼容ie678,主要是一些效果无法实现。

带有图片查看器的常用功能,UI设计和交互灵感来源于google的picasa。

为了保证整体的清洁,界面没有使用任何图标,有需要的可以自行修改css。

不知道什么原因插不了iframe,DEMO

效果图:

代码如下:

js

/**
 * 照片浏览
 * --
 * @author Lianer
 * @version 2015.06.11
 * @description 带有常用照片查看功能,包括缩放、自适应、移动、切换、旋转、下载,ie9+
 * @example
 *   var pv=new PhotoView();
 *   pv.add(["1.jpg", "2.jpg", "3.jpg"]);  // 追加列表
 *   pv.show();   // 显示
 *   pv.close();  // 关闭
 *   pv.aim(1);   // 定位到指定位置
 *   pv.reset();  // 重置
 */

(function () {
  "use strict";
  var PhotoView=window.PhotoView=function () {

    var createElement=function (type, className, parent) {
      var elem=document.createElement(type);
      if(className) elem.className=className;
      if(parent) parent.appendChild(elem);
      return elem;
    };

    var bindWheel=function (elem, fn, cancelBubble) {
      if("onwheel" in document){
        elem.onwheel=handler;
      }
      else if("onmousewheel" in document){
        elem.onmousewheel=handler;
      }
      else{
        return false;
      }
      return true;

      function handler(e) {
        e=window.event||e;
        var deltaX = e.deltaX ||        // wheel
                     -e.wheelDeltaX ||  // onmousewheel
                     0;                 // firefox,DOMMouseScroll不支持2D

        var deltaY = e.deltaY ||        // wheel
                     -e.wheelDeltaY ||  // onmousewheel
                     -e.wheelDelta ||   // 1D
                     e.detail ||        // firefox,DOMMouseScroll
                     0;

        deltaX=deltaX>0?1:deltaX<0?-1:0;
        deltaY=deltaY>0?1:deltaY<0?-1:0;

        fn(deltaY, deltaX, e);

        if(cancelBubble){
          if(e.preventDefault) e.preventDefault();
          if(e.stopPropagation) e.stopPropagation();
          e.cancelBubble=true;
          e.returnValue=false;
          return false;
        }
      }
    };

    var pv=function () {
      this.index=0;
      this.queue=[];
      var elem=this.elem={};
      // 外框
      elem.wrap=createElement("div", "photoview photoview_" + pv.size++);
      elem.wrap.setAttribute("tabindex", "0");
      // 容器
      elem.container=createElement("div", "photoview-container", elem.wrap);
      // 缩放指数
      elem.scaleValue=createElement("div", "photoview-scale-value", elem.container);
      elem.scaleValue.style.display="none";
      // 预览
      elem.view=createElement("div", "photoview-view", elem.container);
      elem.viewCache=createElement("img");
      // 控制栏
      elem.control=createElement("div", "photoview-control", elem.wrap);
      // 控制栏队列
      elem.controlQueue=createElement("div", "photoview-control-queue", elem.control);
      // 编辑
      elem.controlEdit=createElement("div", "photoview-control-edit", elem.control);
      // 放大
      elem.scaleUp=createElement("div", "photoview-scale-up", elem.controlEdit);
      // 缩小
      elem.scaleDown=createElement("div", "photoview-scale-down", elem.controlEdit);
      // 自适应
      elem.scaleAdapt=createElement("div", "photoview-scale-adapt", elem.controlEdit);
      // 上一张
      elem.prev=createElement("div", "photoview-prev", elem.controlEdit);
      // 下一张
      elem.next=createElement("div", "photoview-next", elem.controlEdit);
      // 逆时针旋转
      elem.rotateCCW=createElement("div", "photoview-rotate-ccw", elem.controlEdit);
      // 顺时针旋转
      elem.rotateCW=createElement("div", "photoview-rotate-cw", elem.controlEdit);
      // 下载
      if(window.Blob) elem.download=createElement("div", "photoview-download", elem.controlEdit);
      // 关闭
      elem.close=createElement("div", "photoview-close", elem.wrap);
      elem.close.innerHTML="关闭";

      document.body.appendChild(elem.wrap);

      this.$bind();
    };
    pv.prototype={
      // control定位
      aim: function (n) {
        var _this=this, elem=this.elem, queue=this.queue;
        if(n>queue.length-1){
          n=queue.length;
        }
        else if(n<0){
          n=0;
        }
        var target=elem.controlQueue.querySelectorAll("p");
        if(target){
          target=target[n];
        }
        if(target){
          var last=elem.controlQueue.querySelector(".active");
          if(last){
            last.className="";
          }
          target.className="active";
          elem.controlQueue.style.left=(elem.control.clientWidth/2-target.offsetLeft-target.offsetWidth/2)+"px";
          this.$view(this.queue[n]);
          this.index=n;
        }
        return this;
      },
      // 适应
      $adapt: function () {
        var _this=this, elem=this.elem, target=this.queue[this.index],
          view=elem.view,
          img=elem.viewCache,
          container=elem.container,
          scale=target.scale;

        var imgSize={
          width: img.width,
          height: img.height,
          rate: img.width/img.height
        };
        var conSize={
          width: container.clientWidth,
          height: container.clientHeight,
          rate: container.clientWidth/container.clientHeight
        };

        if(target.scale==null){
          var coverage=0.7;
          if(imgSize.rate>conSize.rate){  // 更宽
            if(imgSize.width>conSize.width*coverage){
              target.scale=conSize.width/imgSize.width*coverage;
            }
            else{
              target.scale=1;
            }
          }
          else{
            if(imgSize.height>conSize.height*coverage){
              target.scale=conSize.height/imgSize.height*coverage;
            }
            else{
              target.scale=1;
            }
          }
        }
        target.width=imgSize.width*target.scale;
        target.height=imgSize.height*target.scale;
        target.left=((conSize.width-imgSize.width*target.scale)/2+target.x);
        target.top=((conSize.height-imgSize.height*target.scale)/2+target.y);
        view.style.width=target.width+"px";
        view.style.height=target.height+"px";
        view.style.left=target.left + "px";
        view.style.top=target.top + "px";
        view.style.transform="rotate(" + target.rotate + "deg)";
      },
      // 追加列表
      add: function () {
        var _this=this, elem=this.elem, queue=this.queue;
        var arg;
        for (var i = 0; arg=arguments[i]; i++) {
          if(!arg){
            return false;
          }
          if(arg.length){
            var a;
            for (var i = 0; a=arg[i]; i++) {
              checkType(a);
            }
          }
          else{
            check(arg);
          }
        }
        function checkType(mixed) {
          if(typeof mixed==="string"){
            var img=document.createElement("img");
            img.src=mixed;
            add(img, null, null);
          }
          else if(mixed.nodeName&&mixed.nodeName.toLowerCase()==="img"){
            add(mixed.getAttribute("data-source")||mixed.src,
              mixed.getAttribute("data-rotate")||null,
              mixed.getAttribute("data-scale")||null);
          }
          else{
            return false;
          }
        }
        function add(src, rotate, scale) {
          var q={
            src: src,  // 图片路径
            rotate: rotate||0,  // 旋转角度
            scale: scale,  // 缩放比例,null时会通过adapt计算以contain
            x: 0,  // x轴偏移
            y: 0   // y轴偏移
          };
          queue.push(q);
          var p=createElement("p", null, elem.controlQueue),
            img=createElement("img");
          p.style.cssText="opacity: 0;background-image: url(" + src + ");";
          p.photoview={
            index: queue.length-1
          };
          img.src=src;
          img.onload=function () {
            p.style.opacity="";
            this.onload=null;
          };
        }
        return this;
      },
      // 绑定事件
      $bind: function () {
        var _this=this, elem=this.elem, queue=this.queue;
        // 关闭
        elem.close.onclick=function () {
          _this.close();
        };
        // 窗口变化
        var resizeTimer=0, resizeHandler=function () {
          clearTimeout(resizeTimer);
          resizeTimer=setTimeout(function () {
            if(!_this.queue.length) return;
            elem.controlQueue.children[_this.index].click();
          }, 200);
        };
        if(window.addEventListener){
          addEventListener("resize", resizeHandler);
        }
        else if(window.attachEvent){
          attachEvent("onresize", resizeHandler);
        }
        // view.onload自适应
        elem.viewCache.onload=function () {
          elem.view.style.backgroundImage=‘url("‘ + this.src + ‘")‘;
          _this.$adapt();
        };
        // view缩放
        var scaleTipTimer=0, showScaleTip=function (scale) {
          if(window.console) console.log(scale);
          elem.scaleValue.innerHTML=parseInt(scale*100)+"%";
          elem.scaleValue.style.display="block";
          clearTimeout(scaleTipTimer);
          clearTimeout(scaleTipTimer+1);
          clearTimeout(scaleTipTimer+2);
          scaleTipTimer=setTimeout(function () {
            elem.scaleValue.style.opacity=1;
          }, 16);
          setTimeout(function () {
            elem.scaleValue.style.opacity=0;
          }, 1200);
          setTimeout(function () {
            elem.scaleValue.style.display="none";
          }, 1300);
        };
        bindWheel(elem.container, function (y, x, e) {
          if(!_this.queue.length) return;
          var target=queue[_this.index],
            rate=1.2;
          if(y>0){
            target.scale=target.scale/rate;
          }
          else if(y<0){
            target.scale=target.scale*rate;
          }

          showScaleTip(target.scale);

          if(e.target===elem.view){
            // view内部定点缩放
            var position={
              // transition中的元素会使getBoundingClientRect、getComputedStyle、offsetLeft等无法取得最终值
              x: e.clientX-target.left-target.width/2,
              y: e.clientY-target.top-target.height/2
            };
            if(y>0){
              target.x=target.x-(position.x/rate-position.x);
              target.y=target.y-(position.y/rate-position.y);
            }
            else if(y<0){
              target.x=target.x-(position.x*rate-position.x);
              target.y=target.y-(position.y*rate-position.y);
            }
          }

          _this.$adapt();
        }, true);
        // view移动
        var moving=false, coord={x: 0, y: 0};
        elem.view.onmousedown=function (e) {
          e=window.event||e;
          moving=true;
          coord.x=e.clientX;
          coord.y=e.clientY;
          elem.view.style.zIndex=2;
        };
        // 离开事件绑定到wrap,防止禁止移动状态下(选中元素拖拽)发生bug
        elem.wrap.onmouseup=elem.wrap.onmouseout=function (e) {
          moving=false;
          elem.view.style.transition="";
          elem.view.style.zIndex="";
        };
        elem.view.onmousemove=function (e) {
          if(!_this.queue.length) return;
          if(moving){
            elem.view.style.transition="none";
            var target=_this.queue[_this.index];
            target.x=target.x+e.clientX-coord.x;
            target.y=target.y+e.clientY-coord.y;
            coord.x=e.clientX;
            coord.y=e.clientY;
            _this.$adapt();
          }
        };
        // 点击queue,定位到目标
        elem.controlQueue.onclick=function (e) {
          e=window.event||e;
          var target=e.srcElement||e.target;
          if(target.nodeName==="P"){
            _this.aim(target.photoview.index);
          }
        };
        // control滚动
        bindWheel(elem.control, function (y, x) {
          if(y>0){
            _this.aim(_this.index+1);
          }
          else if(y<0){
            _this.aim(_this.index-1);
          }
        }, true);
        // 捕获按键
        elem.wrap.onkeydown=function (e) {
          if(e.keyCode===27){
            _this.close();
          }
        };
        // 放大
        elem.scaleUp.onclick=function () {
          if(!_this.queue.length) return;
          var target=queue[_this.index];
          target.scale=target.scale*1.1;
          showScaleTip(target.scale);
          _this.$adapt();
        };
        // 缩小
        elem.scaleDown.onclick=function () {
          if(!_this.queue.length) return;
          var target=queue[_this.index];
          target.scale=target.scale/1.1;
          showScaleTip(target.scale);
          _this.$adapt();
        };
        // 自适应
        elem.scaleAdapt.onclick=function () {
          if(!_this.queue.length) return;
          var target=queue[_this.index];
          target.x=target.y=0;
          if(target.scale===1){
            target.scale=null;
          }
          else{
            target.scale=1;
          }
          _this.$adapt();
        };
        // 上一张
        elem.prev.onclick=function () {
          if(!_this.queue.length) return;
          _this.aim(_this.index-1);
        };
        // 下一张
        elem.next.onclick=function () {
          if(!_this.queue.length) return;
          _this.aim(_this.index+1);
        };
        // 顺时针旋转
        elem.rotateCW.onclick=function () {
          if(!_this.queue.length) return;
          queue[_this.index].rotate=(queue[_this.index].rotate+90)%360;
          _this.$adapt();
        };
        // 逆时针旋转
        elem.rotateCCW.onclick=function () {
          if(!_this.queue.length) return;
          queue[_this.index].rotate=(queue[_this.index].rotate-90)%360;
          _this.$adapt();
        };
        // 下载
        elem.download && (elem.download.onclick=function () {
          if(!_this.queue.length) return;
          var a=document.createElement("a");
          a.href=_this.queue[_this.index].src;
          a.download=/([^\/]+)$/.test(a.href)&&RegExp.$1||new Date().getTime()
          a.click();
        });
      },
      // 隐藏查看器
      close: function () {
        var style=this.elem.wrap.style;
        style.opacity=0;
        setTimeout(function () {
          style.display="none";
        }, 316);
        return this;
      },
      // 销毁
      destory: function () {
        // 暂时只移除dom
        this.elem.wrap.parentNode.removeChild(this.elem.wrap);
        return this;
      },
      // 清空列表
      reset: function () {
        this.queue.length=0;
        this.elem.controlQueue.innerHTML="";
        this.elem.view.style.cssText="";
        return this;
      },
      // 显示查看器
      show: function () {
        var _this=this, elem=this.elem,
          style=elem.wrap.style;
          style.opacity=0;
          style.display="block";
        setTimeout(function () {
          style.opacity=1;
        }, 16);
        _this.aim(0);
        elem.wrap.focus();
        return this;
      },
      // 预览
      $view: function (target) {
        var _this=this, elem=this.elem;
        elem.viewCache.src=target.src;
      }
    };
    pv.size=0;
    return pv;
  }();

})();

css

.photoview{
  display: none;
  position: fixed;left: 0;top: 0;right: 0;bottom: 0;z-index: 100;overflow: hidden;
  background: rgba(0, 0, 0, .8);
  transition: all 0.3s ease-out;
  moz-user-select: -moz-none;
  -moz-user-select: none;
  -o-user-select: none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
.photoview-container{
  position: relative;width: 100%;height: 100%;
}
.photoview-scale-value{
  position: absolute;left: 0;top: 0;right: 0;bottom: 0;margin: auto;z-index: 2;
  width: 3em;height: 20px;line-height: 20px;text-align: center;
  background: rgba(0, 0, 0, .7);color: #fff;border-radius: 20px;
  opacity: 0;
  transition: opacity 0.1s ease-out;
}
.photoview-view{
  position: absolute;
  cursor: all-scroll;
  background-repeat: no-repeat;
  background-position: 50% 50%;
  background-size: 100% 100%;
  box-shadow: 0 5px 25px 0 rgba(0, 0, 0, 0.4);
  transition: width 0.3s ease-out,
    height 0.3s ease-out,
    left 0.3s ease-out,
    top 0.3s ease-out;
}
.photoview-close{position: absolute;right: 10px;top: 10px;color: #fff;cursor: pointer;}
.photoview-control{
  position: absolute;left: 0;right: 0;bottom: 0;height: 120px;
  background-color: rgba(0, 0, 0, .6);opacity: 0;
  transition: all 0.3s ease-out;
}
.photoview-control:hover{opacity: 1;}
.photoview-control-queue{
  position: absolute;left: 0;top: 15px;width: 10000%;height: 60px;
  transition: all 0.4s ease-out;
}
.photoview-control-queue p{
  display: inline-block;width: 60px;height: 60px;margin: 0 2px;background-color: rgba(0, 0, 0, .8);
  background-size: cover;cursor: pointer;
  transition: all 0.2s ease-out;
}
.photoview-control-queue p.active{
  opacity: 1;
}
.photoview-control-edit{
  position: absolute;left: 0;bottom: 15px;width: 100%;height: 15px;
  text-align: center;
}
.photoview-control-edit:before{
  content: "";position: absolute;left: 50%;top: -20px;width: 0;height: 0;margin-left: -10px;overflow: hidden;
  border: 10px solid transparent;border-bottom-color: rgba(255, 255, 255, .7);
}
.photoview-control-edit > div{
  display: inline-block;margin: 0 2px;width: 4em;min-width: 10px;min-height: 10px;
  cursor: pointer;color: #fff;text-align: center;
}
.photoview-scale-up:before{content: "放大";}
.photoview-scale-down:before{content: "缩小";}
.photoview-scale-adapt:before{content: "自适应";}
.photoview-prev:before{content: "上一张";}
.photoview-next:before{content: "下一张";}
.photoview-rotate-ccw:before{content: "逆时针";}
.photoview-rotate-cw:before{content: "顺时针";}
.photoview-download:before{content: "下载";}

demo

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>图片浏览</title>
  <style>
    .list img{float: left;display: block;width: 50px;height: 50px;margin: 2px;}
  </style>
</head>
<body>
  <div id="list" class="list">
    <img src="http://i1.tietuku.com/f424109042f29ed6.jpg" alt="">
    <img src="http://i3.tietuku.com/01585f07bf4308b6.jpg" alt="">
    <img src="http://i3.tietuku.com/dc295d3a37c11bbb.jpg" alt="">
    <img src="http://i3.tietuku.com/20bdc0bcd3e94334.jpg" alt="">
    <img src="http://i3.tietuku.com/a43fac13664dc8ee.jpg" alt="">
  </div>
  <!-- 引入Photoview的css和js -->
  <link rel="stylesheet" href="./photoView.css">
  <script src="./photoview.js"></script>
  <script>
    var pv=new PhotoView(),  // 初始化PhotoView,生成DOM元素
      list=document.querySelector("#list");
    pv.add(list.children);  // 将元素添加到PhotoView
    list.onclick=function (e) {
      e=window.event||e;
      var target=e.srcElement||e.target;
      // 事件委托
      if(target.nodeName==="IMG"){
        pv.show();  // 显示PhotoView
      }
      var index=getIndex(target);
      pv.aim(index);  // 定位到某个图片
    };
    list.children[0].click();

    /**
     * 获取当前元素在兄弟元素中的index
     * @param  {dom} elem 目标元素
     * @return {number}   index
     */
    function getIndex(elem) {
      if(elem.sourceIndex){
        return elem.sourceIndex-elem.parentNode.sourceIndex;
      }
      else{
        var i=0;
        while(elem=elem.previousElementSibling) i++;
        return i;
      }
    }
  </script>

</body>
</html>
时间: 2024-10-12 06:53:30

js图片查看器 - 灵感来自于picasa的相关文章

JS图片查看器

<html> <meta http-equiv="Content-Type" content="text/html; charset=gb2312"> <title>JS网页图片查看器-可控制图片放大缩小还原移动效果</title> <META HTTP-EQUIV="imagetoolbar" CONTENT="no"> <style type="t

强大的jQuery图片查看器插件Viewer.js

简介 Viewer.js 是一款强大的图片查看器 Viewer.js 有以下特点: 支持移动设备触摸事件 支持响应式 支持放大/缩小 支持旋转(类似微博的图片旋转) 支持水平/垂直翻转 支持图片移动 支持键盘 支持全屏幻灯片模式(可做屏保) 支持缩略图 支持标题显示 支持多种自定义事件 Viewer.js 提供了纯 JS 版本和 jQuery 版本,版本名字虽然一样,但代码不一样,不能通用. 在线演示及下载 在线演示:http://www.jqhtml.com/wp-content/upload

jQuery功能强大的图片查看器插件

简要教程 viewer是一款功能强大的图片查看器jQuery插件.它可以实现ACDsee等看图软件的部分功能.它可以对图片进行移动,缩放,旋转,翻转,可以前后浏览一组图片.该图片查看器还支持移动设备,支持键盘控制,功能十分强大. 查看演示      下载插件 安装 可以通过nmp或bower来安装该图片查看器插件. npm install imageviewer bower install imageviewer 复制代码 使用方法 使用该幻灯片插件需要引入jQuery,viewer.css和v

require、backbone等重构手机图片查看器

本文是对之前的部分补充,也是对最近学习require.backbone的一次实例化的实践,希望对正在学习理解中的同学们有帮助 前文请前往:制作手机使用的网页图片查看器 新手机图片查看器 网页部分 require引入是重点,指明了主函数所在文件路径 <!doctype html> <html lang="zh-cn"> <head> <title>webapp图片查看器</title> <meta charset=&quo

制作手机使用的网页图片查看器

这几天抽空在为项目开发一个量身的图片查看器,目前已初步完成需求 开发场景是:在一个多文件下载展示列表中,如检测某些文件为图片时,则点击该文件时打开图片查看器展示该图片,并将列表内其它图片同时展示查看器队列内,可供前后滑动查看及其它附带功能 乍一听功能点似乎有点多而且有些复杂,需要梳理一下 功能点整理 首先,我们要获得点击的图片文件对象及符合条件的图片文件对象集 其次,图片查看器的制作及图片队列展示 然后,图片友好加载方式 最后,图片查看器触摸滑动及滑动后相关功能的实现 简单整理了一下,好像也不多

基于jQuery功能强大的图片查看器插件

viewer是一款功能强大的图片查看器jQuery插件.它可以实现ACDsee等看图软件的部分功能.它可以对图片进行移动,缩放,旋转,翻转,可以前后浏览一组图片.该图片查看器还支持移动设备,支持键盘控制,功能十分强大. 在线预览   源码下载 安装 可以通过nmp或bower来安装该图片查看器插件. npm install imageviewer bower install imageviewer 使用方法 使用该幻灯片插件需要引入jQuery,viewer.css和viewer.js文件. <

网页中的图片查看器viewjs使用

需求分析: 对于网页中的图片进行连续放大(便于用户清晰查看内容).缩小,旋转等操作,可以使用viewjs图片查看器插件实现. viewjs官方网址:https://github.com/fengyuanchen/viewerjs 具体使用方法请参照官网说明. 下面做2个简单的示例: 1.示例一:单一图片 1 <!DOCTYPE html> 2 <html lang="zh"> 3 4 <head> 5 <meta charset="U

Android笔记二十七.Bitmap之简易图片查看器

转载请表明出处:http://blog.csdn.net/u012637501(嵌入式_小J的天空) 为了增强用户之间的交互,Android系统中提供了一些API和部件给我们开发美观有趣的应用.比如Android系统提供了ImageView来显示静态图片.AnimationDrawble来开发逐帧动画以及通过Animation对普通图片使用不减动画等.另外,Android应用中的图片不仅包括*.png.*.jpg.*.gif等格式的位图,也包括使用XML资源文件定义的各种Drawable对象.关

Qt项目实战2:简单的图片查看器(1)

在博文http://www.cnblogs.com/hancq/p/5817108.html中介绍了使用空的Qt项目创建带有菜单栏.工具栏的界面. 这里,使用一个简单的图片查看器项目,来熟悉一下Qt的图片显示和基本操作. 该项目分为两部分: (1)实现图片的打开.关闭.居中显示.上一张/下一张切换 (2)实现图片的放大.缩小.左旋.右旋.另存为等操作 需要用的Qt类: QFileDialog QImage QPixmap QFileInfo 使用空的Qt项目创建带有菜单栏和工具栏的界面的操作参考