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中实现命名空间
Nov 23 Javascript
js点击页面其它地方将某个显示的DIV隐藏
Jul 12 Javascript
js DOM 元素ID就是全局变量
Sep 20 Javascript
javascript修改IMG标签的src问题
Mar 28 Javascript
我的Node.js学习之路(二)NPM模块管理
Jul 06 Javascript
jQuery EasyUI datagrid实现本地分页的方法
Feb 13 Javascript
Bootstrap树形控件使用方法详解
Jan 27 Javascript
JS中如何实现Laravel的route函数详解
Feb 12 Javascript
js中document.referrer实现移动端返回上一页
Feb 22 Javascript
JavaScript实现三级级联特效
Nov 05 Javascript
js prototype和__proto__的关系是什么
Aug 23 Javascript
Bootstrap FileInput实现图片上传功能
Jan 28 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 基本语法格式
2009/12/15 PHP
php 魔术函数使用说明
2010/02/21 PHP
实用PHP会员权限控制实现原理分析
2011/05/29 PHP
PHP二维数组的去重问题解析
2011/07/17 PHP
zf框架的registry(注册表)使用示例
2014/03/13 PHP
基于ThinkPHP5框架使用QueryList爬取并存入mysql数据库操作示例
2019/05/25 PHP
javascript 原型继承介绍
2011/08/30 Javascript
使用jQuery清空file文件域的解决方案
2013/04/12 Javascript
JS HTML5 音乐天气播放器(Ajax获取天气信息)
2013/05/26 Javascript
Javascript自定义函数判断网站访问类型是PC还是移动终端
2014/01/10 Javascript
通过复制Table生成word和excel的javascript代码
2014/01/20 Javascript
谈一谈bootstrap响应式布局
2016/05/23 Javascript
AngularJS入门教程之Scope(作用域)
2016/07/27 Javascript
VUE 更好的 ajax 上传处理 axios.js实现代码
2017/05/10 Javascript
jQuery获取table表中的td标签(实例讲解)
2017/07/28 jQuery
JS中min函数实例讲解
2019/02/18 Javascript
使用Vue开发自己的Chrome扩展程序过程详解
2019/06/21 Javascript
Vue实现多页签组件
2021/01/14 Vue.js
python操作字典类型的常用方法(推荐)
2016/05/16 Python
python3.6+opencv3.4实现鼠标交互查看图片像素
2018/02/26 Python
python中logging模块的一些简单用法的使用
2019/02/22 Python
pycharm无法导入本地模块的解决方式
2020/02/12 Python
Python进程的通信Queue、Pipe实例分析
2020/03/30 Python
Jupyter加载文件的实现方法
2020/04/14 Python
Python读写csv文件流程及异常解决
2020/10/20 Python
任意一块网页内容实现“活”的背景(目前火狐浏览器专有)
2014/05/07 HTML / CSS
CSS3绘制不规则图形的一些方法示例
2015/11/07 HTML / CSS
LEGO玩具英国官方商店:LEGO Shop GB
2018/03/27 全球购物
Lookfantastic意大利官网:英国知名美妆购物网站
2019/05/31 全球购物
顶撞领导检讨书
2014/01/29 职场文书
三好学生个人先进事迹材料
2014/05/17 职场文书
大学生标准自荐书
2014/06/15 职场文书
医院财务人员岗位职责
2015/04/14 职场文书
格林童话读书笔记
2015/06/30 职场文书
golang中的并发和并行
2021/05/08 Golang
JVM之方法返回地址详解
2022/02/28 Java/Android