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 相关文章推荐
jQuery 关于伪类选择符的使用说明
Apr 24 Javascript
javascript基于DOM实现权限选择实例分析
May 14 Javascript
jQuery满意度星级评价插件特效代码分享
Aug 19 Javascript
jQuery简单实现input文本框内灰色提示文本效果的方法
Dec 02 Javascript
BootStrap下jQuery自动完成的样式调整
May 30 Javascript
原生js实现瀑布流布局
Mar 08 Javascript
js实现图片旋转 js滚动鼠标中间对图片放大缩小
Jul 05 Javascript
详解Vue用自定义指令完成一个下拉菜单(select组件)
Oct 31 Javascript
vue项目在安卓低版本机显示空白的原因分析(两种)
Sep 04 Javascript
实例讲解JavaScript截取字符串
Nov 30 Javascript
模块化react-router配置方法详解
Jun 03 Javascript
uni-app实现获取验证码倒计时功能
Nov 01 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
PHP对象Object的概念 介绍
2012/06/14 PHP
解析phpstorm + xdebug 远程断点调试
2013/06/20 PHP
PHP连接SQL server数据库测试脚本运行实例
2020/08/24 PHP
动态控制Table的js代码
2007/03/07 Javascript
Javascript new关键字的玄机 以及其它
2010/08/25 Javascript
js简单设置与使用cookie的方法
2016/01/22 Javascript
JavaScript下的时间格式处理函数Date.prototype.format
2016/01/27 Javascript
深入浅析JavaScript中的constructor
2016/04/19 Javascript
下一代Bootstrap的5个特点 超酷炫!
2016/06/17 Javascript
AngularJS API之copy深拷贝详解及实例
2016/09/14 Javascript
网络传输协议(http协议)
2016/11/18 Javascript
微信小程序开发的四十个技术窍门总结(推荐)
2017/01/23 Javascript
jquery仿ps颜色拾取功能
2017/03/08 Javascript
基于JS实现限时抢购倒计时间表代码
2017/05/09 Javascript
vue2导航根据路由传值,而改变导航内容的实例
2017/11/10 Javascript
JSON生成Form表单的方法示例
2018/11/21 Javascript
vue eslint简要配置教程详解
2019/07/26 Javascript
微信小程序全选多选效果实现代码解析
2020/01/21 Javascript
vue中移动端调取本地的复制的文本方式
2020/07/18 Javascript
Python实现的文本简单可逆加密算法示例
2017/05/18 Python
python得到单词模式的示例
2018/10/15 Python
pip安装python库的方法总结
2019/08/02 Python
Tensorflow之梯度裁剪的实现示例
2020/03/08 Python
conda安装tensorflow和conda常用命令小结
2021/02/20 Python
Claire’s法国:时尚配饰、美容、珠宝、头发
2021/01/16 全球购物
如何强制垃圾回收
2015/10/06 面试题
《世界多美呀》教学反思
2014/03/02 职场文书
水污染治理工程专业求职信
2014/06/14 职场文书
妇联领导班子剖析材料
2014/08/21 职场文书
放飞梦想演讲稿200字
2014/08/26 职场文书
法律专业大学生职业生涯规划书:向目标一步步迈进
2014/09/22 职场文书
一个家长教育孩子的心得体会
2016/01/15 职场文书
公司趣味运动会开幕词
2016/03/04 职场文书
写一个Python脚本下载哔哩哔哩舞蹈区的所有视频
2021/05/31 Python
Python 键盘事件详解
2021/11/11 Python
详解JavaScript的计时器和按钮效果设置
2022/02/18 Javascript