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实现仿新浪微博获取文本框能输入的字数代码
Feb 22 Javascript
js实现遮罩层划出效果是生成div而不是显示
Jul 29 Javascript
JavaScript的代码编写格式规范指南
Dec 07 Javascript
jQuery EasyUI右键菜单实现关闭标签/选项卡
Oct 10 Javascript
详解照片瀑布流效果(js,jquery分别实现与知识点总结)
Jan 01 Javascript
原生js获取浏览器窗口及元素宽高常用方法集合
Jan 18 Javascript
vue头部导航动态点击处理方法
Nov 02 Javascript
vsCode安装使用教程和插件安装方法
Aug 24 Javascript
vue设计一个倒计时秒杀的组件详解
Apr 06 Javascript
使用VUE实现在table中文字信息超过5个隐藏鼠标移到时弹窗显示全部
Sep 16 Javascript
Vue实现开心消消乐游戏算法
Oct 22 Javascript
JavaScript实现网页下拉菜单效果
Nov 20 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 高手之路(三)
2006/10/09 PHP
浅谈PHP与C#的值类型指向区别的详解
2013/05/21 PHP
浅析echo(),print(),print_r(),return之间的区别
2013/11/27 PHP
php设置允许大文件上传示例代码
2014/03/10 PHP
php格式化json函数示例代码
2016/05/12 PHP
PHP程序员学习使用Swoole的理由
2018/06/24 PHP
javascript中巧用“闭包”实现程序的暂停执行功能
2007/04/04 Javascript
JQuery插件开发示例代码
2013/11/06 Javascript
jQuery中each()方法用法实例
2014/12/27 Javascript
js实现瀑布流的三种方式比较
2020/06/28 Javascript
讲解JavaScript的Backbone.js框架的MVC结构设计理念
2016/02/14 Javascript
AngularGauge 属性解析详解
2016/09/06 Javascript
详解AngularJs ui-router 路由的简单介绍
2017/04/26 Javascript
jQuery Collapse1.1.0折叠插件简单使用
2017/08/28 jQuery
JS实现的图片选择顺序切换和循环切换功能示例【测试可用】
2018/12/28 Javascript
详解小程序如何避免多次点击,重复触发事件
2019/04/08 Javascript
判断JavaScript中的两个变量是否相等的操作符
2019/12/21 Javascript
Vue 封装防刷新考试倒计时组件的实现
2020/06/05 Javascript
keep-Alive搭配vue-router实现缓存页面效果的示例代码
2020/06/24 Javascript
[02:33]2018DOTA2亚洲邀请赛赛前采访——LGD
2018/04/04 DOTA
Python中列表、字典、元组数据结构的简单学习笔记
2016/03/20 Python
python监控键盘输入实例代码
2018/02/09 Python
Django 过滤器汇总及自定义过滤器使用详解
2019/07/19 Python
Pytorch中.new()的作用详解
2020/02/18 Python
最小二乘法及其python实现详解
2020/02/24 Python
python实现飞船大战
2020/04/24 Python
通信工程专业毕业生推荐信
2013/12/25 职场文书
工作失误检讨书范文大全
2014/01/13 职场文书
餐饮企业总经理岗位职责范文
2014/02/18 职场文书
医院反腐倡廉演讲稿
2014/09/16 职场文书
买房协议书范本
2014/10/23 职场文书
六年级语文下册教学计划
2015/01/22 职场文书
老公保证书怎么写
2015/02/26 职场文书
初三化学教学反思
2016/02/22 职场文书
2019年怎样写好导游词?
2019/07/02 职场文书
MySQL的安装与配置详细教程
2021/06/26 MySQL