jQuery实现模拟flash头像裁切上传功能示例


Posted in Javascript onDecember 11, 2016

本文实例讲述了jQuery实现模拟flash头像裁切上传功能。分享给大家供大家参考,具体如下:

是的,jq已经有类似的插件了,或者干脆用flash算了,为什么我还要自己写?因为造(wo)轮(bu)子(hui)也(flash)是一个学习的过程,轮子不会造,将来怎么造飞机?先来一张最终效果图:

jQuery实现模拟flash头像裁切上传功能示例

一、大概思路

用js来做这个效果,先得将图片A上传到服务器,关于异步上传的插件有很多,不用插件也可以参考本人上一篇博客用纯js的方式上传,上传之后显示到页面里,由于上传的图片尺寸各不相同,要完整地显示图片,就要将上传后的图片用css控制按比例缩放显示,然后通过矩形选框选择需要的部分,用js获取矩形选框的左上角坐标,加上选框的宽高按比例计算后传给后台,后台程序根据所传参数来裁切得到图片B后返回到前台并将上传的原图A删除,节省空间。

二、分析

将效果图分为左右两部分,先看左边,由一张图片加一个矩形选区组成,图片和选区之间有一层半透明的遮罩,但是这样的话会连选区部分一块遮住,就没有上面这种框选出来的效果了,事实上结构是这样的:由下往上分别是1图片层,2遮罩层,3选区层(一个div,绝对定位),4图片层(绝对定位)。第1层和第4层的图片是一样的,大小及left、top值也一样,给第3层选区层加个overflow:hidden,就呈现出了上面的效果,虚线边框及拖拽的8个点后文会讲到。下图比较直观地说明的它们的层级关系,第3层灰色部分为overflow:hidden隐藏的部分:

jQuery实现模拟flash头像裁切上传功能示例

做完图发现左右两边框的位置不一样,但重在说明原理。接下来,选区部分可以拖动,用到拖拽原理:鼠标按下,记录var disx=event.clientX,var disy=event.clientY,拖动,计算当前event.clientX与disx的差值为x,当前event.clientY与disy的差值为y,设置第4层图片的left值为图片当前offsetLeft+disx,top值为offsetTop+disy。如选区往左移动10px,由于第4层只能在第1层范围内移动,那么刚好第4层的left值等于负的第3层的left值,top值同理。拖拽原理图:

jQuery实现模拟flash头像裁切上传功能示例

选区大小是可以按比例改变的,这就需要用到选区周围的8个点,如下图可以分为4个部分:

jQuery实现模拟flash头像裁切上传功能示例

每个部分里的点触发的事件是一样的,4个部分触发的事件都是改变选区大小,不一样的地方在于第1部分会同时改变选区的left和top值,第2和第4部分分别只改变的是选区的top、left值,第3部分不会改变选区的left和top值。4个部分原理都一样,拿第1部分说事,点击第1部分的点往左上角拖动,选区变大的同时设置其left和top值(会减小),而left减小的值刚好等于选区增大的值,这个值的计算方法同拖拽原理。拖拽过程中需要限制范围,不能超出整个图片的范围。

选中需要截取的部分后,获取当前选区(第4层)的左上角的坐标,即第4层的offsetLeft、offsetTop值,再获取选区的宽高,这4个值不能直接往后台传,因为此时的图片可能是被缩放过的,而后台是根据原图尺寸来截取的,那么需要在图片上传完之后获取图片原始宽高,与页面中图片显示宽高得出一个比例,将这4个值乘以这个比例得出的值才是后台需要的。

至于选区的边框,做得简单点可以直接设置border:1px dashed #fff,更好的方法是放四个position:absolute的div,分别固定在选区的上下左右,上下宽100%,高1px,左右宽1px,高100%,背景设为一个波浪纹的gif图片,repeat,出来的效果很是惊艳!

右边部分3张图片仅仅是展示用,显示的内容是左边选区选中的部分,而选区的大小是可以改变的,所以右边的图片大小及位置是随着选区的变化而变化。选择图片上传后,选区有个默认宽高,右边3个框宽高是固定的,根据选区宽与右边三个框的宽分别相除得出的比例可以算出右边三个框内的图片应该显示的尺寸,显示原理同左边,相比左边只是少了第1、2层。

这种方式的优点是纯js,兼容性也好,另外还可以做个特性检测,支持HTML5的浏览器可以直接在前端切割图片,缺点是裁切之前要选将图片上传。源码晚点贴上来。

三、源码

<!DOCTYPE html>
<html>
<head>
  <title></title>
  <script src="js/jquery.min.js"></script>
  <style>
  *{margin: 0;padding: 0}
  </style>
</head>
<body>
<style>
.uploadHead{max-width: 800px;}
.clearfix{clear: both;overflow: hidden;position: relative;zoom: 1;}
.l{float: left;}.r{float: right;}
.uploadHead h3{color: #19110a; text-decoration: none; border-bottom: #BFC9CB 1px solid; padding: 10px 0;margin-bottom: 30px;}
.preview{width: 400px; height: 400px;padding: 1px; border: #B8B8B8 1px dashed;margin-right: 18px; position: relative;}
.canvas{background-color: #E8F3F7;width:392px; height: 392px; margin: 4px; text-align: center; position: relative; overflow: hidden;}
.canvas .mask{width: 100%;height: 100%; background: #000; opacity: 0.7; filter:alpha(opacity=70); position: absolute;}
.photoBox p{width: 100px; padding-left: 16px; float: left; color: #aeacab;}
.photoBox .size{width: 100%;}
.p_180,.p_80,.p_70{position: relative;border: #B5B5B5 1px solid;overflow: hidden;}
.p_180 img,.p_80 img,.p_70 img{position: absolute; left: 0;top: 0;}
.p_180{width: 180px; height: 210px; margin-bottom: 20px;}
.p_80{width: 80px; height: 80px; margin-bottom: 20px;}
.p_70{width: 70px; height: 70px;}
.cutImg{text-align: center;margin-top: 10px;}
.cutImg input{width: 80px; height: 30px; border: none;margin: 10px 20px; font-size: 16px; color: #fff; cursor: pointer; border-radius: 2px;}
.cutImg .save{background-color: #e34128;}
.cutImg .cancel{background-color: #a19f9f;}
.checkImg{width: 192px; height: 192px;position: absolute; left: 50%; top: 50%; margin:-96px 0 0 -96px;z-index: 9; display: none;}
.checkImg p{color: #898989; font-size: 14px; text-align: center;}
.checkImg .checkLocalImg{width: 132px; height: 42px;margin:50px 30px 20px; background: url(img/checkImg.png) center; font-size: 14px; color: #fff; cursor: pointer;}
.imgBox{position: relative;margin: 0 auto;display: none1; overflow: hidden; }
.cutImgBox{ width: 180px; height: 210px; position: absolute; z-index: 2; }
.cutImgBox img{ position: absolute; left: 0px; top:0px;}
.imgCon{position: relative;width: 100%;height: 100%;overflow: hidden;cursor: pointer; z-index: 1;}
.imgCon .lineBg{background:#fff url(img/jcrop.gif) center repeat; opacity: 0.6; filter:alpha(opacity:60); position: absolute; z-index: 3;}
.imgCon .tandb{width: 100%;height: 1px;}
.imgCon .landr{height: 100%;width: 1px;}
.imgCon .right{right: 0;}
.imgCon .bottom{bottom: 0;}
.cSize{width: 100%; height: 100%; position: absolute;left: 0;top: 0; cursor: move; z-index: 8;}
.cSize .btn{width: 7px; height: 7px; border: #eee 1px solid; background-color: #333; opacity: 0.5;filter:alpha(opacity:50); position: absolute;}
.cSize .lt{left: -4px; top: -4px;cursor: nw-resize;}
.cSize .tc{left:50%; margin-left: -4px;top:-4px;cursor: n-resize;}
.cSize .rt{right: -4px; top:-4px;cursor: ne-resize;}
.cSize .rc{right: -4px; top:50%;margin-top: -4px;cursor: e-resize;}
.cSize .rb{right: -4px; bottom: -4px;cursor: se-resize;}
.cSize .bc{bottom: -4px; left: 50%;margin-left: -4px;cursor: n-resize;}
.cSize .lb{left: -4px; bottom: -4px;cursor: sw-resize;}
.cSize .lc{left: -4px;top:50%;margin-top: -4px;cursor: e-resize;}
.width_392{max-width: 392px; max-height: 392px; z-index: 1;}
.fileInput{width: 100%; height: 50px;top: 50px;font-size: 100px; position: absolute; opacity: 0; filter:alpha(opacity=0); cursor: pointer;}
.ie8Drag{width: 100%; height: 100%; position: absolute;left: 0;top: 0; z-index: 99; background-color: #000; opacity: 0; filter:alpha(opacity=0);}
</style>
<!-- 头像上传 By 王美建 2014-10-9 10:45:02 -->
<script type="text/javascript" >
//图片裁切对象
function CutImg(){
  this.init();
};
CutImg.prototype.init=function(opt){
  var that=this;
  this.con=$('.cutImgBox')[0];this.img=$('.cutImgBox img:last')[0];
  this.imgBox=$('#imgBox');this.defaultWidth=180;this.defaultHeight=210;
  this.scalex=this.defaultWidth/this.defaultHeight;this.scaley=this.defaultHeight/this.defaultWidth;
  that.drag().setSize().cSize().cPosition();;
}
// 拖拽
CutImg.prototype.drag=function(){
  var that=this;
  this.con.onmousedown=function(e){
    var e=e||window.event,target=e.target||e.srcElement;
    if($(target).hasClass('btn')) return;
    var disx=e.clientX-that.con.offsetLeft,disy=e.clientY-that.con.offsetTop;
    document.onmousemove=function(ev){
      var ev=ev||event,L,T;
      L=ev.clientX-disx;
      T=ev.clientY-disy;
      if(L<0){
        L=0;
      }else if(L>that.con.parentNode.offsetWidth-that.con.offsetWidth){
        L=that.con.parentNode.offsetWidth-that.con.offsetWidth;
      };
      if(T<0){
        T=0;
      }else if(T>that.con.parentNode.offsetHeight-that.con.offsetHeight){
        T=that.con.parentNode.offsetHeight-that.con.offsetHeight;
      };
      that.con.style.left=L+'px';
      that.con.style.top=T+'px';
      that.img.style.left=-that.con.offsetLeft+'px';
      that.img.style.top=-that.con.offsetTop+'px';
      that.cPosition();
    }
    document.onmouseup=function(){
      document.onmousemove=null;
      document.onmouseup=null;
    }
    return false;
  }
  return this;
};
// 改变图片尺寸
CutImg.prototype.setSize=function(){
  var that=this.con;
  $('.p_180 img').css('width',that.parentNode.offsetWidth*180/that.offsetWidth);
  $('.p_80 img').css('width',that.parentNode.offsetWidth*80/that.offsetWidth);
  $('.p_70 img').css('width',that.parentNode.offsetWidth*70/that.offsetWidth);
  return this;
};
// 改变图片位置
CutImg.prototype.cPosition=function(){
  this.setPosition( $('.p_180'),180,210 );
  this.setPosition( $('.p_80'),80,80 );
  this.setPosition( $('.p_70'),70,70 );
  return this;
};
// 设置三幅图片显示位置
CutImg.prototype.setPosition=function(obj,w,h){
  var that=this.con;
  obj.find('img').css({
    'left':-w*that.offsetLeft/that.offsetWidth,
    'top':-h*that.offsetTop/that.offsetHeight
  });
  return this;
};
// 保存截取后的头像
CutImg.prototype.saveImg=function() {
  var x=0,y=0,w=180,h=210,that=this,cutObj=$('.cutImgBox')[0];
  w=parseInt( this.oldW*cutObj.offsetWidth/that.imgBox.width() );
  h=parseInt( w*that.scaley );
  x=parseInt(cutObj.offsetLeft/(that.imgBox.width()-that.con.offsetWidth)*(this.oldW-w));
  y=parseInt(cutObj.offsetTop/(that.imgBox.height()-that.con.offsetHeight)*(this.oldH-h));
  x=x?x=x:x=0;y=y?y=y:y=0; //x/y可能为NaN
  //x,y,w,h分别为后端需要的坐标及宽高
}
// 改变选区大小
CutImg.prototype.cSize=function(){
  var that=this.con,This=this;
  var thatImg=this.img;
  $('.cSize .btn').each(function() {
    var obj=this;
    obj.onmousedown=function(e) {
      var e=e||window.event;
      var disx=e.clientX,disy=e.clientY;
      var disw=that.offsetWidth,dish=that.offsetHeight,disl=that.offsetLeft,dist=that.offsetTop;
      document.onmousemove=function(ev) {
        var ev=ev||window.event,dirx=ev.clientX-disx,diry=ev.clientY-disy;
        var minW=6,minH=7,L,T;
        //点击第1部分改变选取尺寸
        if( $(obj).hasClass('t1') ){
          L=disl+dirx;T=dish-(disw-dirx)*This.scaley+dist;
          if( L<0||T<0 ||disw-dirx<minW||(disw-dirx)*This.scaley<minH) return;
          $(that).css({
            'left':L,
            'top':T,
            'width':disw-dirx,
            'height':(disw-dirx)*This.scaley
          })
          $(thatImg).css({
            'left':-L,
            'top':-that.offsetTop
          })
        }
        //点击第2部分改变选取尺寸
        if( $(obj).hasClass('t2') ){
          if( dist+diry<0||(dish-diry)*This.scalex<minW||dish-diry<minH||(dish-diry)*This.scalex+that.offsetLeft>that.parentNode.offsetWidth )return;
          $(that).css({
            'top':dist+diry,
            'width':(dish-diry)*This.scalex,
            'height':dish-diry
          })
          $(thatImg).css({
            'top':-that.offsetTop
          })
        }
        //点击第3部分改变选取尺寸
        if( $(obj).hasClass('t3') ){
          if( disw+dirx+that.offsetLeft>that.parentNode.offsetWidth||(disw+dirx)*This.scaley+that.offsetTop>that.parentNode.offsetHeight||disw+dirx<minW||(disw+dirx)*This.scaley<minH ) return;
          $(that).css({
            'width':disw+dirx,
            'height':(disw+dirx)*This.scaley
          })
        }
        //点击第4部分改变选取尺寸
        if( $(obj).hasClass('t4') ){
          if( disl+dirx<0||(disw-dirx)*This.scaley+that.offsetTop>that.parentNode.offsetHeight||disw-dirx<minW||(disw-dirx)*This.scaley<minH ) return;
          $(that).css({
            'left':disl+dirx,
            'width':disw-dirx,
            'height':(disw-dirx)*This.scaley
          })
          $(thatImg).css({
            'left':-that.offsetLeft
          })
        }
        This.setSize().cPosition();
        return false;
      };
      document.onmouseup=function(e) {
        document.onmousemove=null;
        document.onmouseup=null;
      }
      return;
    };
  });
};
$(function(){
  var oCutImg=new CutImg();
})
</script>
<div class="e_box uploadHead" id="uploadHead">
  <h3>上传真实头像</h3>
  <div class="e_con">
    <div class="previewBox l">
      <div class="preview">
        <div class="checkImg" id="checkImg">
          <input type="file" id="headImgInput" name="img" accept="image/jpg,image/jpeg,image/png" dir="rtl" title="选择本地照片" class="fileInput" />
           <input type="button" value="选择本地照片" class="checkLocalImg" />
           <p>支持JPG/JPEG/PNG格式</p>
         </div>
        <div class="canvas">
          <div class="imgBox" id="imgBox">
            <div class="cutImgBox">
              <div class="imgCon">
                <div class="lineBg tandb"></div>
                <div class="lineBg tandb bottom"></div>
                <div class="lineBg landr"></div>
                <div class="lineBg landr right"></div>
                <img class="width_392 staPhoto" src="img/1.png" />
                <div class="ie8Drag"></div>
              </div>
              <div class="cSize">
                <div class="btn lt t1"></div><div class="btn tc t2"></div>
                <div class="btn rt t2"></div><div class="btn rc t3"></div>
                <div class="btn rb t3"></div><div class="btn bc t3"></div>
                <div class="btn lb t4"></div><div class="btn lc t4"></div>
              </div>
            </div>
            <div id="mask" class="mask"></div>
            <img class="width_392 staPhoto" src="img/1.png" />
          </div>
        </div>
      </div>
      <div class="cutImg">
        <input type="button" class="save" value="保存" >
        <input type="button" class="cancel" id="cancelUp" value="取消" >
      </div>
    </div>
    <div class="photoBox l">
      <div class="size">
        <div class="p_180 l"><img class="staPhoto" src="img/1.png" /></div><p>大尺寸头像 180×210像素</p>
      </div>
      <div class="size clearfix">
        <div class="p_80 l"><img class="staPhoto" src="img/1.png" /></div><p>中尺寸头像 80×80像素</p>
      </div>
      <div class="size">
        <div class="p_70 l"><img class="staPhoto" src="img/1.png" /></div><p>小尺寸头像 70×70像素</p>
      </div>
    </div>
  </div>
</div>
</body>
</html>

希望本文所述对大家jQuery程序设计有所帮助。

Javascript 相关文章推荐
JavaScript面向对象编程
Mar 02 Javascript
JavaScript解析URL参数示例代码
Aug 12 Javascript
使用js判断TextBox控件值改变然后出发事件
Mar 07 Javascript
jQuery prototype冲突的2种解决方法(附demo示例下载)
Jan 21 Javascript
require.js 加载 vue组件 r.js 合并压缩的实例
Oct 14 Javascript
JS实现禁止鼠标右键的功能
Oct 15 Javascript
基于jQuery实现数字滚动效果
Jan 16 Javascript
非常实用的vue导航钩子
Mar 20 Javascript
详解node child_process模块学习笔记
Jan 24 Javascript
webpack配置proxyTable时pathRewrite无效的解决方法
Dec 13 Javascript
使用Vue实现移动端左滑删除效果附源码
May 16 Javascript
vue中 v-for循环的用法详解
Feb 19 Javascript
javascript实现将数字转成千分位的方法小结【5种方式】
Dec 11 #Javascript
JavaScript获取服务器时间的方法详解
Dec 11 #Javascript
基于jQuery实现的查看全文功能【实用】
Dec 11 #Javascript
AngularJS过滤器filter用法分析
Dec 11 #Javascript
jquery判断页面网址是否有效的两种方法
Dec 11 #Javascript
JavaScript奇技淫巧44招【实用】
Dec 11 #Javascript
利用JS判断鼠标移入元素的方向
Dec 11 #Javascript
You might like
便携利器 — TECSUN PL-365简评
2021/03/02 无线电
php面向对象全攻略 (十一)__toString()用法 克隆对象 __call处理调用错误
2009/09/30 PHP
PHP使用GIFEncoder类生成的GIF动态图片验证码
2014/07/01 PHP
smarty模板引擎之内建函数用法
2015/03/30 PHP
Laravel向公共模板赋值方法总结
2019/06/25 PHP
JQuery的read函数与js的onload不同方式实现
2013/03/18 Javascript
动态加载script文件的两种方法
2013/08/15 Javascript
地址栏传递中文参数乱码在js里用escape转码
2013/08/28 Javascript
用jquery写的一个万年历(自写)
2014/01/20 Javascript
js实现滚动条滚动到页面底部继续加载
2015/12/19 Javascript
js实现纯前端的图片预览
2016/04/27 Javascript
AngularJS全局scope与Isolate scope通信用法示例
2016/11/22 Javascript
JavaScript中匿名函数的递归调用
2017/01/22 Javascript
jQuery代码实现实时获取时间
2017/01/29 Javascript
原生js仿浏览器滚动条效果
2017/03/02 Javascript
NodeJS实现视频转码的示例代码
2017/11/18 NodeJs
jquery自定义显示消息数量
2017/12/19 jQuery
Vue cli构建及项目打包以及出现的问题解决
2018/08/27 Javascript
Vue组件通信入门之Provide和Inject机制
2019/12/29 Javascript
原生JS实现微信通讯录
2020/06/18 Javascript
vue项目中js-cookie的使用存储token操作
2020/11/13 Javascript
简单分析Python中用fork()函数生成的子进程
2015/05/04 Python
python3解析库BeautifulSoup4的安装配置与基本用法
2018/06/26 Python
Python中fnmatch模块的使用详情
2018/11/30 Python
Python不同目录间进行模块调用的实现方法
2019/01/29 Python
Python使用字典实现的简单记事本功能示例
2019/08/15 Python
Python环境下安装PyGame和PyOpenGL的方法
2020/03/25 Python
Python爬虫scrapy框架Cookie池(微博Cookie池)的使用
2021/01/13 Python
学校办公室主任职责
2013/12/27 职场文书
公司门卫岗位职责
2014/03/15 职场文书
马丁路德金演讲稿
2014/05/19 职场文书
公安机关纪律作风整顿个人剖析材料材料
2014/10/10 职场文书
学校运动会广播稿
2014/10/11 职场文书
党员检讨书
2014/10/13 职场文书
JS如何实现基于websocket的多端桥接平台
2021/05/14 Javascript
苹果的回收机器人可以通过拆解iPhone获取大量的金和铜并外公布了环境保护最新进展
2022/04/21 数码科技