推荐

这一篇文章是早年为了解决图片裁剪的探索性文章,现在已经开放出了falsh版及html5版本的图片裁剪插件,各位有时间可以看看:
浮士德html5图片裁剪器2016开源版
浮士德头像裁剪flash版2016福利版
上面两个解决方案已经经过多个项目的成功应用,适用于低级浏览器及现代浏览器,ipad,android,iphone4s,iphone5,iphone5s,iphone6等设备等。

前言

将相应html及js脚本放出来,注意,这是原型,并非立刻可以使用的东西。

html界面

<!DOCTYPE HTML>
<html>
<head><title>个人相片</title><meta charset="utf-8" /><meta content="width=device-width, initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport"/><meta content="yes" name="apple-mobile-web-app-capable"><meta content="black" name="apple-mobile-web-app-status-bar-style"><meta content="telephone=yes" name="format-detection"><meta content="email=no" name="format-detection"><script type="text/javascript" src="/static/lib/jquery-1.11.0.min.js"></script><script type="text/javascript" src="/static/lib/util.min.js"></script><script type="text/javascript" src="/static/lib/exif.min.js"></script><script type="text/javascript" src="/static/vendor/ImgTools/ImageOrientationFix.js"></script><script type="text/javascript" src="/static/vendor/ImgTools/ImageResizer.js"></script><script type="text/javascript" src="/static/vendor/layer1.8/layer/layer.min.js"></script><link href="/static/vendor/jQuery.showLoading/css/showLoading.css" rel="stylesheet" media="screen" /><script type="text/javascript" src="/static/vendor/jQuery.showLoading/js/jquery.showLoading.js"></script><script type="text/javascript" src="ImageEditor.js"></script>
</head>
<body><div><h3>请选择图片:</h3><div><input type="file" style="" id="uploadImage"  accept="image/*"></div>
</div><h2>裁剪器区域</h2>
<div id="div_cropper"><div ui="image-editor-panel"><canvas ui="background-layout" style="position: absolute; "></canvas><canvas ui="cutter-layout" style="position: absolute; "></canvas><canvas ui="operation-layout" style="position: absolute; cursor: pointer; "></canvas></div>
</div>
<div class="image-editor-tool-bar">
<button type="button" id="btn_zoom_in">放大图片</button>
<button type="button" id="btn_zoom_out">缩小图片</button>
<button type="button" id="btn_turn_left">向左转</button>
<button type="button" id="btn_turn_right">向右转</button>
</div><div><input type="button" id="btn_save" value="保存图片"></div>
<h2>预览结果:</h2>
<img src="" id="preview_img"/><div id="tips" style="color:green;"></div><script type="text/javascript" src="example2.js"></script>
<div id="debug" style="color: green;"></div>
<img src="daojian_1.png" id="img_test" style="display: none;">
<div  style="width:1200px;height: 800px; border: 1px solid red;display: none;"><canvas id="test_canvas" width="1200" height="800"></canvas>
</div>
<script type="text/javascript">var _img=document.getElementById("img_test");var _canvas=document.getElementById("test_canvas");var _context=_canvas.getContext("2d");</script>
</body></html>

核心组件代码

/*** 图片裁剪组件,必须跟jquery exif ImageOrientationFix.js 等一起用。*/
function ImageEditor(opts){var settings={cutWidth:150 //裁剪框的宽度。,cutHeight:200 //裁剪框的高度,containerWidth:800 //容器宽度,containerHeight:600 //容器高度,imageShowWidth:400 //图片默认显示的宽度【会按照要求的宽度及高度等比缩放】,imageShowHeight:500 //图片默认显示的高度【会按照要求的宽度及高度等比缩放】,containerElement:"" //容器的默认元素,jquery元素或者原生dom元素。,showCutterInFirst:true //是否第一时间显示裁剪框---注意,true意味着即使用户没有选择图片,背景层没有显示图片裁剪框也显示。,saveMode:"ratio" //裁剪图片的模式有两种,一种是 ratio即,在原图片上面按照比例裁剪图片,尺寸不一定固定,一种是 size,严格按照裁剪框的尺寸来保存图片。};$.extend(settings,opts);function _debug(str){return;var _div=$("<div></div>");_div.text(str);$("#debug").append(_div);};function _clearDebug(){return;$("#debug").empty();}var __me=this;//--定义必须用到的dom元素的句柄。var containerElement=$(settings.containerElement);//整个容器对象,var panel=containerElement.find('[ui="image-editor-panel"]');//这是包装几个画布的面板。var canvas_bg=containerElement.find('[ui="background-layout"]')[0];//原生画布对象,这是背景画布,用于绘制图片及缩放,转向等操作。var canvas_cutter=containerElement.find("[ui='cutter-layout']")[0];//原生画布对象,这是裁剪框对象。用于绘制遮罩层及裁剪框。var canvas_operation=containerElement.find("[ui='operation-layout']")[0];//原生画布对象,这是操作层对象,用于接受用户的鼠标及手势,然后触发各种事件。var context_cutter=canvas_cutter.getContext("2d");var context_bg=canvas_bg.getContext("2d");var data_origin_image="";//原始图片数据,原始是指经过了ios bug修复剂orientation方向修复以后的base64格式的数据。注意,必要时,假如图片太大,可以先压缩到某个尺寸。var image_background=new Image();//新建一个背景图的元素用于处理图片旋转等操作。//--下面预先将背景图片的三个方向都先计算出来。//window.image_backgroud=image_background;//--内部运行时数据var data_cutter={location:{x:0,y:0},size:{w:0,h:0},ratio:1};//裁剪框数据var data_bg={angle:0,img_bg_0:""//0度图片。,img_bg_90:""//90度图片,img_bg_180:""//180度旋转图片,img_bg_270:""//270度旋转图片,location:{x:0,y:0},size:{w:0,h:0},originSize:{w:0,h:0},zoom:1};//背景层数据-这个作为原始备份,告诉系统应该如何转换数据。var data_bg_trans={angle:0,location:{x:0,y:0},size:{w:0,h:0},originSize:{w:0,h:0},zoom:1};//在非正常情况下的位置的数据等,例如,转换成为90,180,270,0等的数据,var data_container={size:{w:0,h:0}};//容器数据var data_op={panelInfo:{absoluteTop:0,absoluteLeft:0,offsetWidth:0,offsetHeight:0},isInDrag:false,beginPoint:{x:0,y:0},point1:{x:0,y:0},point2:{x:0,y:0}//判断是否在裁剪框里面。,isInCut:false};//操控层数据。var appData={panelSize:{w:0,h:0},hasImage:false //是否已经选择了图片,容器里面有没有图片。};//app内部数据var innerTools={browser:{versions:function(){var u = navigator.userAgent, app = navigator.appVersion;return {         //移动终端浏览器版本信息trident: u.indexOf('Trident') > -1, //IE内核presto: u.indexOf('Presto') > -1, //opera内核webKit: u.indexOf('AppleWebKit') > -1, //苹果、谷歌内核gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //火狐内核mobile: !!u.match(/AppleWebKit.*Mobile.*/), //是否为移动终端ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //ios终端android: u.indexOf('Android') > -1 || u.indexOf('Linux') > -1, //android终端或uc浏览器iPhone: u.indexOf('iPhone') > -1 , //是否为iPhone或者QQHD浏览器iPad: u.indexOf('iPad') > -1, //是否iPadwebApp: u.indexOf('Safari') == -1 //是否web应该程序,没有头部与底部};}(),language:(navigator.browserLanguage || navigator.language).toLowerCase()},GetAbsoluteLocationEx:function(element){if(arguments.length!=1||element==null){return null;}var elmt=element;var offsetTop=elmt.offsetTop;var offsetLeft=elmt.offsetLeft;var offsetWidth=elmt.offsetWidth;var offsetHeight=elmt.offsetHeight;while(elmt=elmt.offsetParent){// add this judgeif(elmt.style.position=='absolute'||elmt.style.position=='relative'||(elmt.style.overflow!='visible'&&elmt.style.overflow!='')){break;}offsetTop+=elmt.offsetTop;offsetLeft+=elmt.offsetLeft;}var _info={absoluteTop:offsetTop,absoluteLeft:offsetLeft,offsetWidth:offsetWidth,offsetHeight:offsetHeight};return _info;}};var app={init:function(){var _child=this;appData.panelSize.w=settings.containerWidth;appData.panelSize.h=settings.containerHeight;_child.render_ui();_child.init_cutter();_child.initOperationLayout();}//--设定容器及其他相关dom的样式。,render_ui:function(){containerElement.css({"width":settings.containerWidth+"px","height":settings.containerHeight+"px"});canvas_bg.width=settings.containerWidth;canvas_bg.height=settings.containerHeight;canvas_cutter.width=settings.containerWidth;canvas_cutter.height=settings.containerHeight;canvas_operation.width=settings.containerWidth;canvas_operation.height=settings.containerHeight;}//--计算相关数据--初始化裁剪框。,init_cutter:function(){var _child=this;data_cutter.size.w=settings.cutWidth;data_cutter.size.h=settings.cutHeight;data_cutter.ratio=settings.cutWidth/settings.cutHeight;//--计算中间点。var middle_x=appData.panelSize.w/2;var middle_y=appData.panelSize.h/2;var _x=middle_x-data_cutter.size.w/2;var _y=middle_y-data_cutter.size.h/2;data_cutter.location.x=_x;data_cutter.location.y=_y;if(settings.showCutterInFirst){_child.updateCutter();}}//--根据cutter的数据更新裁剪框层。,updateCutter:function(){var _child=this;console.log("目前更新裁剪框,数据"+data_cutter.location.x+":x "+data_cutter.location.y);_child.__clear_cutter_canvas();_child.__draw_mask();_child.__draw_cutter();}//--清空cutter画布的内容。,__clear_cutter_canvas:function(){context_cutter.clearRect(0,0,canvas_cutter.width,canvas_cutter.height);}//--渲染cutter,__draw_cutter:function(){//context_cutter.strokeStyle = '#ffffff';context_cutter.lineWidth = 2;context_cutter.strokeRect(data_cutter.location.x,data_cutter.location.y,data_cutter.size.w,data_cutter.size.h);context_cutter.clearRect(data_cutter.location.x,data_cutter.location.y,data_cutter.size.w,data_cutter.size.h);//context_cutter.clearRect(0,0,canvas_cutter.width,canvas_cutter.height);}//--渲染遮罩层,__draw_mask:function(){context_cutter.fillStyle = 'rgba(0, 0, 0, 0.3)';context_cutter.fillRect(0, 0, canvas_cutter.width,canvas_cutter.height);}//--对外接口,拖动裁剪框。,MoveCutter:function(pos_x,pos_y){var _child=this;data_cutter.location.x=pos_x;data_cutter.location.y=pos_y;_child.updateCutter();}//--移动裁剪框。,MoveCutterByDelta:function(delta_x,delta_y){var _child=this;var _new_x= data_cutter.location.x+delta_x;var _new_y=data_cutter.location.y+delta_y;//--判断边界。if(_new_x<0){_new_x=0;}if(_new_y<0){_new_y=0;}if(_new_x>(appData.panelSize.w-data_cutter.size.w)){_new_x=appData.panelSize.w-data_cutter.size.w;}if(_new_y>(appData.panelSize.h-data_cutter.size.h)){_new_y=appData.panelSize.h-data_cutter.size.h;}console.log("新的裁剪框位置是:"+_new_x+" X "+_new_y);_child.MoveCutter(_new_x,_new_y);}//--初始化背景层。背景层需要获得图片的base64字符串先。,init_background:function(base_64_image){var _child=this;_child.init_bg_data(base_64_image,function(finalBase64Image){_child.updateBackground();});}//--背景图片等图片处理。数据处理等。,init_bg_data:function(originBase64,callback){ImageOrientationFix({image:originBase64,imgType:"base64",onFix:function(base64_str){data_origin_image=base64_str;image_background.onload=function(){appData.hasImage=true;//--获取相关数据及计算。data_bg.originSize.w=image_background.naturalWidth;data_bg.originSize.h=image_background.naturalHeight;data_bg.angle=0;data_bg.location.x=0;data_bg.location.y=0;data_bg.zoom=1;data_bg.size.w=data_bg.originSize.w;data_bg.size.h=data_bg.originSize.h;image_background.onload=function(){};console.log("bg层数据");console.log(data_bg);//--按照当前显示区域,尽量显示全部图片,计算显示尺寸。if(data_bg.originSize.w>appData.panelSize.w||data_bg.originSize.h>appData.panelSize.h){var _max_scale= Math.max(data_bg.originSize.w/appData.panelSize.w,data_bg.originSize.h/appData.panelSize.h);var _current_w=data_bg.originSize.w/_max_scale;var _current_h=data_bg.originSize.h/_max_scale;data_bg.zoom=1/_max_scale;data_bg.size.w=_current_w;data_bg.size.h=_current_h;console.log("bg层计算得到的可以显示区域是:");console.log(data_bg);}//--计算背景层应该显示的位置,尽量放到中间。data_bg.location.x=(appData.panelSize.w-data_bg.size.w)/2;data_bg.location.y=(appData.panelSize.h-data_bg.size.h)/2;//--计算各个状体的图片。data_bg.img_bg_0=image_background;data_bg.img_bg_90=new Image();data_bg.img_bg_180=new Image();data_bg.img_bg_270=new Image();var __canvas=document.createElement("canvas");var __context=__canvas.getContext("2d");//$(document.body).prepend(__canvas);//--90度的。__canvas.width=data_bg.originSize.h;__canvas.height=data_bg.originSize.w;__context.save();__context.rotate(Math.PI/2);__context.drawImage(image_background,0,0-data_bg.originSize.h);__context.restore();data_bg.img_bg_90.src= __canvas.toDataURL();__context.clearRect(0,0,__canvas.width,__canvas.height);//--180度的。__canvas.width=data_bg.originSize.w;__canvas.height=data_bg.originSize.h;__context.save();__context.rotate(Math.PI);__context.drawImage(image_background,0-data_bg.originSize.w,0-data_bg.originSize.h);__context.restore();data_bg.img_bg_180.src= __canvas.toDataURL();__context.clearRect(0,0,__canvas.width,__canvas.height);//--270度的。__canvas.width=data_bg.originSize.h;__canvas.height=data_bg.originSize.w;__context.save();__context.rotate(Math.PI*1.5);__context.drawImage(image_background,0-data_bg.originSize.w,0);__context.restore();data_bg.img_bg_270.src= __canvas.toDataURL();__context.clearRect(0,0,__canvas.width,__canvas.height);//--trans 背景数据。data_bg_trans.angle=data_bg.angle;data_bg_trans.location.x=data_bg.location.x;data_bg_trans.location.y=data_bg.location.y;data_bg_trans.size.w=data_bg.size.w;data_bg_trans.size.h=data_bg.size.h;data_bg_trans.originSize.w=data_bg.originSize.w;data_bg_trans.originSize.h=data_bg.originSize.h;data_bg_trans.zoom=data_bg.zoom;if(callback){callback(base64_str);}};image_background.src=base64_str;}});}//--清空背景。,clear_background:function(){context_bg.clearRect(0,0,canvas_bg.width,canvas_bg.height);}//--根据相关数据更新背景层图像。,updateBackground:function(){var _child=this;_child.clear_background();if(data_bg_trans.angle==0||data_bg_trans.angle==360){//context.drawImage(img,0,0,img.width,img.height,imgX,imgY,img.width*imgScale,img.height*imgScale);
//                context_bg.drawImage(image_background,data_bg.location.x,data_bg.location.y,data_bg.originSize.w,data_bg.originSize.h,0,0,data_bg.size.w,data_bg.size.h);context_bg.drawImage(image_background,0,0,data_bg_trans.originSize.w,data_bg_trans.originSize.h,data_bg_trans.location.x,data_bg_trans.location.y,data_bg_trans.size.w,data_bg_trans.size.h);}else if(data_bg_trans.angle==90){context_bg.drawImage(data_bg.img_bg_90,0,0,data_bg_trans.originSize.w,data_bg_trans.originSize.h,data_bg_trans.location.x,data_bg_trans.location.y,data_bg_trans.size.w,data_bg_trans.size.h);}else if(data_bg_trans.angle==180){context_bg.drawImage(data_bg.img_bg_180,0,0,data_bg_trans.originSize.w,data_bg_trans.originSize.h,data_bg_trans.location.x,data_bg_trans.location.y,data_bg_trans.size.w,data_bg_trans.size.h);}else if(data_bg_trans.angle==270){context_bg.drawImage(data_bg.img_bg_270,0,0,data_bg_trans.originSize.w,data_bg_trans.originSize.h,data_bg_trans.location.x,data_bg_trans.location.y,data_bg_trans.size.w,data_bg_trans.size.h);}}//--底层api,根据位置移动图片。,MoveBackground:function(pos_x,pos_y){data_bg_trans.location.x=pos_x;data_bg_trans.location.y=pos_y;var _child=this;_child.updateBackground();}//--通过偏移量来移动图片。,MoveBackgroundByDelta:function(delta_x,delta_y){var _child=this;var _new_x=data_bg_trans.location.x+delta_x;var _new_y=data_bg_trans.location.y+delta_y;var _min_x=0;var _max_x=0;//--宽度边界判断。//--假如图片宽度竟然比裁剪框要小,那么宽度最小值是0,if(data_bg_trans.size.w<=data_cutter.size.w){_min_x=0;_max_x=appData.panelSize.w-data_bg_trans.size.w;}//--假如现在的图片宽度比整个容器宽度要小或者要大,那么就有://else if(data_bg_trans.size.w<=appData.panelSize.w){//计算得出这时候的x最大值最小值。//var __min_x=//}//--假如现在的图片宽度比整个容器宽度要小或者要大,那么就有:else{_min_x=data_cutter.size.w-data_bg_trans.size.w;_max_x=appData.panelSize.w-data_cutter.size.w;}//--高度边界判断。var _min_y=0;var _max_y=0;if(data_bg_trans.size.h<=data_cutter.size.h){_min_y=0;_max_y=appData.panelSize.h-data_bg_trans.size.h;}else{_min_y=data_cutter.size.h-data_bg_trans.size.h;_max_y=appData.panelSize.h-data_cutter.size.h;}if(_new_x<_min_x){_new_x=_min_x;}else if(_new_x>_max_x){_new_x=_max_x;}if(_new_y<_min_y){_new_y=_min_y;}else if(_new_y>_max_y){_new_y=_max_y;}console.log("新的图片位置是:"+_new_x+" X "+_new_y);//_child.MoveCutter(_new_x,_new_y);_child.MoveBackground(_new_x,_new_y);}//--底层api,缩放图片。,ScaleBackground:function(_scale){//--在缩放前首先计算得到图片的中心点。var _middle_x=0;var _middle_y=0;//--正常状体下面。_middle_x=data_bg_trans.location.x+data_bg_trans.size.w/2;_middle_y=data_bg_trans.location.y+data_bg_trans.size.h/2;data_bg_trans.zoom=_scale;data_bg_trans.size.w=data_bg_trans.originSize.w*_scale;data_bg_trans.size.h=data_bg_trans.originSize.h*_scale;var __x=_middle_x-(data_bg_trans.size.w)/2;var __y=_middle_y-(data_bg_trans.size.h)/2;data_bg_trans.location.x=__x;data_bg_trans.location.y=__y;var _child=this;_child.updateBackground();}//--旋转。,RotateImage:function(angle){if(angle!=0&&angle!=90&&angle!=180&&angle!=270&&angle!=360){console.log("不支持该角度。");return;}var preAngle=data_bg_trans.angle;//--计算原本的中心点。var _middle_x=0;var _middle_y=0;//--正常状体下面。_middle_x=data_bg_trans.location.x+data_bg_trans.size.w/2;_middle_y=data_bg_trans.location.y+data_bg_trans.size.h/2;//--那么一旦改变了角度,需要将各个坐标系都转换过来。if(angle==0||angle==360){data_bg_trans.angle=0;data_bg_trans.originSize.w=data_bg.originSize.w;data_bg_trans.originSize.h=data_bg.originSize.h;}else if(angle==90){data_bg_trans.angle=90;data_bg_trans.originSize.w=data_bg.originSize.h;data_bg_trans.originSize.h=data_bg.originSize.w;}else if(angle==180){data_bg_trans.angle=180;data_bg_trans.originSize.w=data_bg.originSize.w;data_bg_trans.originSize.h=data_bg.originSize.h;}else if(angle==270){data_bg_trans.angle=270;data_bg_trans.originSize.w=data_bg.originSize.h;data_bg_trans.originSize.h=data_bg.originSize.w;}data_bg_trans.size.w=data_bg_trans.originSize.w*data_bg_trans.zoom;data_bg_trans.size.h=data_bg_trans.originSize.h*data_bg_trans.zoom;data_bg_trans.location.x=_middle_x-(data_bg_trans.size.w)/2;data_bg_trans.location.y=_middle_y-(data_bg_trans.size.h)/2;data_bg_trans.angle=angle;var _child=this;_child.updateBackground();}//--初始化操控层。,initOperationLayout:function(){var _child=this;data_op.panelInfo=innerTools.GetAbsoluteLocationEx(canvas_operation);console.log("当前panel的位置信息。");console.log(data_op.panelInfo);console.log(innerTools.browser);if(innerTools.browser.versions.mobile==true||innerTools.browser.versions.mobile==true||innerTools.browser.versions.android||innerTools.browser.versions.iPhone){_child._init_mobile_operations();}else{_child._init_pc_operations();}}//--pc上面的控制事件。,_init_pc_operations:function(){//o是移动对象var _child=this;$(canvas_operation).mousedown(function(event){if(appData.hasImage==false){return;}data_op.isInDrag=true;console.log("canvas operation mouse down:");data_op.panelInfo=innerTools.GetAbsoluteLocationEx(canvas_operation);if(data_op.panelInfo.absoluteLeft==0&&data_op.panelInfo.absoluteTop==0&&data_op.panelInfo.offsetHeight==0&&data_op.panelInfo.offsetWidth==0){}//params.flag = true;if (!event) {event = window.event;//防止IE文字选中canvas_operation.onselectstart = function () {return false;}}var e = event;var params_currentX = e.clientX;var params_currentY = e.clientY;data_op.beginPoint.x=params_currentX;data_op.beginPoint.y=params_currentY;var realX=params_currentX-data_op.panelInfo.absoluteLeft;var realY=params_currentY-data_op.panelInfo.absoluteTop;_clearDebug();_debug("未处理前,realX realY是:"+realX+" x "+realY);//注意,上面没有考虑到有滚动条的状态,现在添加对滚动条的处理。//--notice,假如页面有滚动条,每次都滚到不同位置的话,那么这个定位可能会有问题,于是我们需要进行处理。var _scrollTop=$(document).scrollTop();if(_scrollTop>0){realY=realY+_scrollTop;}var _scrollLeft=$(document).scrollLeft();if(_scrollLeft>0){realX=realX+_scrollLeft;}data_op.point1.x=params_currentX;data_op.point1.y=params_currentY;data_op.isInCut=_child.isPointInCutter(realX,realY);_debug("scroll top left信息:"+_scrollTop+" x "+_scrollLeft);_debug("当前panel位置信息是:"+JSON.stringify(data_op.panelInfo));_debug("realX realY是:"+realX+" x "+realY);_debug("裁剪框位置是:"+JSON.stringify(data_cutter));console.log("当前坐标");console.log(params_currentX+"x"+params_currentY);if(data_op.isInCut){console.log("%c 在裁剪框里面。","color:green;");}});$(document).mouseup(function(){if(appData.hasImage==false){return;}data_op.isInDrag=false;console.log("document 鼠标移动上去了。 mouse up");});$(document).mousemove(function (event) {if(appData.hasImage==false){return;}if(data_op.isInDrag==false){return;}var e = event ? event : window.event;var nowX = e.clientX, nowY = e.clientY;console.log("%c 移动事件 mousemove中,位置:"+nowX+" x "+nowY+" 是否在裁剪框里面:"+data_op.isInCut,"color:red");// var disX = nowX - params.currentX, disY = nowY - params.currentY;//target.style.left = parseInt(params.left) + disX + "px";//target.style.top = parseInt(params.top) + disY + "px";//--计算位移量。data_op.point2.x=nowX;data_op.point2.y=nowY;var delta_x=data_op.point2.x-data_op.point1.x;var delta_y=data_op.point2.y-data_op.point1.y;data_op.point1.x=nowX;data_op.point1.y=nowY;if(data_op.isInCut==true){//--已经console.log("现在在重新渲染裁剪框了。"+delta_x+" x "+delta_y);_child.MoveCutterByDelta(delta_x,delta_y);}else{_child.MoveBackgroundByDelta(delta_x,delta_y);}});}//--手机端移动端的事件。,_init_mobile_operations:function(){var _child=this;canvas_operation.addEventListener("touchstart",function(event){if(appData.hasImage==false){return;}var beginX=event.changedTouches[0].pageX;var beginY=event.changedTouches[0].pageY;data_op.isInDrag=true;console.log("canvas operation touch start");data_op.panelInfo=innerTools.GetAbsoluteLocationEx(canvas_operation);if(data_op.panelInfo.absoluteLeft==0&&data_op.panelInfo.absoluteTop==0&&data_op.panelInfo.offsetHeight==0&&data_op.panelInfo.offsetWidth==0){// data_op.panelInfo=innerTools.GetAbsoluteLocationEx(canvas_operation);}var e = event;var params_currentX = beginX;var params_currentY = beginY;data_op.beginPoint.x=params_currentX;data_op.beginPoint.y=params_currentY;var realX=params_currentX-data_op.panelInfo.absoluteLeft;var realY=params_currentY-data_op.panelInfo.absoluteTop;_clearDebug();_debug("未处理前,realX realY是:"+realX+" x "+realY);//注意,上面没有考虑到有滚动条的状态,现在添加对滚动条的处理。//--notice,假如页面有滚动条,每次都滚到不同位置的话,那么这个定位可能会有问题,于是我们需要进行处理。var _scrollTop=$(document).scrollTop();if(_scrollTop>0){//realY=realY+_scrollTop; 移动端不用管,似乎得到的是正确的。}var _scrollLeft=$(document).scrollLeft();if(_scrollLeft>0){// realX=realX+_scrollLeft; 移动端不用管,似乎得到的是正确的。}_debug("scroll top left信息:"+_scrollTop+" x "+_scrollLeft);_debug("当前panel位置信息是:"+JSON.stringify(data_op.panelInfo));_debug("realX realY是:"+realX+" x "+realY);_debug("裁剪框位置是:"+JSON.stringify(data_cutter));data_op.point1.x=params_currentX;data_op.point1.y=params_currentY;data_op.isInCut=_child.isPointInCutter(realX,realY);console.log("当前坐标");console.log(params_currentX+"x"+params_currentY);if(data_op.isInCut){console.log("%c 在裁剪框里面。","color:green;");}event.preventDefault();event.stopPropagation();},false);canvas_operation.addEventListener("touchmove",function(event){if(appData.hasImage==false){return;}if(data_op.isInDrag==false){return;}event.preventDefault();event.stopPropagation();var e = event ? event : window.event;var beginX=event.changedTouches[0].pageX;var beginY=event.changedTouches[0].pageY;var nowX = beginX, nowY = beginY;console.log("%c 移动事件 mousemove中,位置:"+nowX+" x "+nowY+" 是否在裁剪框里面:"+data_op.isInCut,"color:red");// var disX = nowX - params.currentX, disY = nowY - params.currentY;//target.style.left = parseInt(params.left) + disX + "px";//target.style.top = parseInt(params.top) + disY + "px";//--计算位移量。data_op.point2.x=nowX;data_op.point2.y=nowY;var delta_x=data_op.point2.x-data_op.point1.x;var delta_y=data_op.point2.y-data_op.point1.y;data_op.point1.x=nowX;data_op.point1.y=nowY;if(data_op.isInCut==true){//--已经console.log("现在在重新渲染裁剪框了。"+delta_x+" x "+delta_y);_child.MoveCutterByDelta(delta_x,delta_y);}else{_child.MoveBackgroundByDelta(delta_x,delta_y);}},false);canvas_operation.addEventListener("touchend",function(event){if(appData.hasImage==false){return;}data_op.isInDrag=false;event.preventDefault();event.stopPropagation();},false);}//--判断某个点是否在裁剪框里面。,isPointInCutter:function(point_x,point_y){var min_x=data_cutter.location.x;var min_y=data_cutter.location.y;var max_x=min_x+data_cutter.size.w;var max_y=min_y+data_cutter.size.h;if(point_x<min_x||point_x>max_x||point_y<min_y||point_y>max_y){return false;}else{return true;}},_cut_by_sizing:function(callback){var __canvas = document.createElement("canvas");var __context = __canvas.getContext("2d");__canvas.width = data_cutter.size.w;__canvas.height = data_cutter.size.h;var _image=new Image();_image.onload=function(){__context.drawImage(_image,data_cutter.location.x,data_cutter.location.y,data_cutter.size.w,data_cutter.size.h,0,0,data_cutter.size.w,data_cutter.size.h);if(callback){var _base64=__canvas.toDataURL("image/jpeg");callback(_base64);}};_image.src=canvas_bg.toDataURL("image/jpeg");},_cut_by_ratio:function(callback){//--根据当前操作界面上的数据,换算成为图片上真实尺寸。var _real_x_in_img=data_cutter.location.x-data_bg_trans.location.x;var _real_y_in_img=data_cutter.location.y-data_bg_trans.location.y;_real_x_in_img=_real_x_in_img/data_bg_trans.zoom;_real_y_in_img=_real_y_in_img/data_bg_trans.zoom;var _real_w=data_cutter.size.w/data_bg_trans.zoom;var _real_h=data_cutter.size.h/data_bg_trans.zoom;var __canvas = document.createElement("canvas");var __context = __canvas.getContext("2d");__canvas.width = _real_w;__canvas.height = _real_h;var _image;if(data_bg_trans.angle==0||data_bg_trans.angle==360){_image=data_bg.img_bg_0;}else if(data_bg_trans.angle==90){_image=data_bg.img_bg_90;}else if(data_bg_trans.angle==180){_image=data_bg.img_bg_180;}else if(data_bg_trans.angle==270){_image=data_bg.img_bg_270;}__context.drawImage(_image,_real_x_in_img,_real_y_in_img,_real_w,_real_h,0,0,_real_w,_real_h);var _base64=__canvas.toDataURL("image/jpeg");callback(_base64);}//--裁剪图像,,cut:function(callback){var _child=this;if(settings.saveMode=="ratio"){_child._cut_by_ratio(callback);}else{_child._cut_by_sizing(callback);}}};app.init();//初始化控件。this.init=function(base64_image_str){console.log("初始化。");app.init_background(base64_image_str);app.updateCutter();};//--对外接口,移动裁剪框。this.MoveCutter=function(pos_x,pos_y){//--这里至少需要进行边界碰撞的判断。app.MoveCutter(pos_x,pos_y);};//--对外接口,移动图片。this.MoveImage=function(pos_x,pos_y){//--这里至少需要进行边界碰撞的判断。app.MoveBackground(pos_x,pos_y);};//--对外接口,缩放图片、this.ScaleImage=function(_scale){app.ScaleBackground(_scale);};this.RotateImage=function(angle){app.RotateImage(angle);};//--是否已经有图片了。this.hasImage=function(){return appData.hasImage;};this.show_data_cutter=function(){console.log(data_cutter);};//--获取目前的角度。this.getImageAngle=function(){return data_bg_trans.angle;};//--获取图片的缩放倍率。this.getImageRotation=function(){return data_bg_trans.angle;};//--获取缩放倍率。this.getImageZoom=function(){return data_bg_trans.zoom;};this.cutImg=function(callback){app.cut(callback);};}

demo脚本

var _e_width=$(window).width()-200;
var _e_height=$(window).height()-200;
if(_e_width>800){_e_width=800;
}
else if(_e_width<450){_e_width=450;
}
/*
if(_e_height>600){_e_height=600;
}
else if(_e_height<400){_e_height=400;
}*/
var _image_editor={};
try{_image_editor =new ImageEditor({cutWidth:150 //裁剪框的宽度。,cutHeight:200 //裁剪框的高度,containerWidth:_e_width //容器宽度,containerHeight:_e_height //容器高度,imageShowWidth:400 //图片默认显示的宽度【会按照要求的宽度及高度等比缩放】,imageShowHeight:500 //图片默认显示的高度【会按照要求的宽度及高度等比缩放】,containerElement:$("#div_cropper") //容器的默认元素,jquery元素或者原生dom元素。
});
}
catch (ed){alert(ed);
}//--逻辑方法定义
$("#uploadImage").change(function(){if (document.getElementById("uploadImage").files.length === 0) {layer.alert("请选择图片!",3);return; }var oFile = document.getElementById("uploadImage").files[0];//if (!rFilter.test(oFile.type)) { alert("You must select a valid image file!"); return; }/*  if(oFile.size>5*1024*1024){message(myCache.par.lang,{cn:"照片上传:文件不能超过5MB!请使用容量更小的照片。",en:"证书上传:文件不能超过100K!"})changePanel("result");return;}*/if(!new RegExp("(jpg|jpeg|gif|png)+","gi").test(oFile.type)){layer.alert("照片上传:文件类型必须是JPG、JPEG、PNG或GIF!",3);return;}var reader = new FileReader();reader.onload =function(e){var _img_str=e.target.result;// img 元素_image_editor.init(_img_str);};reader.readAsDataURL(oFile);return;
});$("#btn_zoom_out").click(function(){if(_image_editor.hasImage()==false){alert("请先选择图片!");return;}var _zoom=_image_editor.getImageZoom();if(_zoom<0.5){alert("最小倍率是0.5,无法继续缩小");return;}_image_editor.ScaleImage(_zoom-0.2);
});$("#btn_zoom_in").click(function(){if(_image_editor.hasImage()==false){alert("请先选择图片!");return;}var _zoom=_image_editor.getImageZoom();if(_zoom>4){alert("最大倍率是4,无法继续放大");return;}_image_editor.ScaleImage(_zoom+0.2);
});$("#btn_turn_left").click(function(){if(_image_editor.hasImage()==false){alert("请先选择图片!");return;}var _angle=_image_editor.getImageAngle();_angle=(_angle+360)-90;_angle=(_angle)%360;_image_editor.RotateImage(_angle);
});$("#btn_turn_right").click(function(){if(_image_editor.hasImage()==false){alert("请先选择图片!");return;}var _angle=_image_editor.getImageAngle();_angle=(_angle+0)+90;_angle=(_angle)%360;_image_editor.RotateImage(_angle);
});$("#btn_save").click(function(){if(_image_editor.hasImage()==false){alert("请先选择图片!");return;}_image_editor.cutImg(function(imgStr){$("#preview_img").attr("src",imgStr);});
});

未完待续,下一篇是实际应用效果

html5图片裁剪控件原型【含缩放,旋转,拖动功能】---2、核心代码相关推荐

  1. 第一站小红书图片裁剪控件之二,自定义CoordinatorLayout联动效果

    本篇续: 第一站小红书图片裁剪控件,深度解析大厂炫酷控件 先来看看几张效果图: emmmm,想感受高清丝滑的动画效果,有以下两种方式: https://github.com/HpWens/MeiWid ...

  2. CropImageView android上的一个图片裁剪控件

    CropImageView **文前:**本文非常容易让读者看的云里雾里,建议直接看效果图,觉得有用就去看源码吧. CropImageView的原型来自Cropimage_demo,是android上 ...

  3. Android开发技巧——定制仿微信图片裁剪控件

    拍照--裁剪,或者是选择图片--裁剪,是我们设置头像或上传图片时经常需要的一组操作.上篇讲了Camera的使用,这篇讲一下我对图片裁剪的实现. 背景 下面的需求都来自产品. 裁剪图片要像微信那样,拖动 ...

  4. 第一站小红书图片裁剪控件,深度解析大厂炫酷控件

    先来看两张效果图: 哈哈,就是这样了.效果差了一些,感兴趣的小伙伴们可以运行代码感受丝滑与弹性.前段时间在竞品小红书上看到了这样的效果:图片可以跟随手指移动,双指可以(无限)放大,缩小,还可以挤压,手 ...

  5. 像小红书一样的图片裁剪控件联动效果

    今日科技快讯 据CNBC报道,美国法官已经要求特斯拉首席执行官埃隆·马斯克(Elon Musk)在未来两周内设法与美国证券交易委员会(SEC)达成和解协议.否则,法院将决定是否判马斯克犯有藐视法庭罪. ...

  6. 第一站仿小红书图片裁剪控件,深度解析大厂炫酷控件

    先来看两张效果图: 哈哈,就是这样了.效果差了一些,感兴趣的小伙伴们可以运行代码感受丝滑与弹性.前段时间在竞品小红书上看到了这样的效果:图片可以跟随手指移动,双指可以(无限)放大,缩小,还可以挤压,手 ...

  7. Android实现图片滚动控件,含页签功能,让你的应用像淘宝一样炫起来

    转载请注明出处:http://blog.csdn.net/sinyu890807/article/details/8769904 首先题外话,今天早上起床的时候,手滑一下把我的手机甩了出去,结果陪伴我 ...

  8. 浮士德html5图片裁剪器2016开源版

    前言 最近刚刚好整理浮士德头像裁剪的flash版本,为了某些低级浏览器的兼容着想,既然已经做好了flash版本了,那么,现代浏览器的html5版本和ipad版,移动版也要做一些处理和打包. 兼容性 兼 ...

  9. vue 移动端头像裁剪_Vue 头像裁剪控件

    使用 基于canvas撸的一个 Vue头像裁剪控件 PC端,鼠标左键移动.滚轮缩放.右键旋转 移动端,单指移动,双指移动.缩放.旋转 属性 backBoxSize 背景方格大小 默认值:10 back ...

最新文章

  1. CentOS安装新版RabbitMQ解决Erlang 19.3版本依赖
  2. [python教程入门学习]Python是什么?
  3. 【放置奇兵】天六水晶和心三水晶(宝石、心灵水晶)
  4. ajax技术书,ajax技术
  5. 软件项目开发之 软件过程RUP初探
  6. 计算机控制中mcu,MCU学习1:单片机控制应用很广,它在智能控制中起什么作用?...
  7. linux生成一个list文件,Linux 获取文件名称生成列表 txt - create_filelist
  8. 口红机源码运营版对接CC支付源码
  9. 昨日关注-Domain Services
  10. 常见卫星汇总--期待大神补充下载地址
  11. 国科大-模式识别与机器学习(计算机科学与技术学院)-习题解答参考
  12. ramda 函数 list
  13. t’触发器真值表和状态方程_T触发器的特性表.ppt
  14. cocos creator pc web端 全屏
  15. 家里装电线时,为啥说“走顶”比“走地”好
  16. OLEDB简介,OLEDB与ODBC的关系
  17. 如何安装projectlombok
  18. 通过指定cellid获取周围cellid信息,改变指定cellid的颜色
  19. monaco-editor(code编辑器插件)使用及常用配置与方法
  20. windows中盘符的概念

热门文章

  1. html编辑器自定义脚本,ckeditor自定义插件使用方法详解
  2. Towards Universal Object Detection by Domain Attention翻译
  3. dell940服务器系统备份备份,Dell PowerVault TL1000
  4. 需要一个3d人模型定穴位脚本也就是需要一个
  5. iOS安全- 网络封包分析工具Charles
  6. 微信开发者工具:errMsg: “request:fail invalid url “xxx“
  7. 【设计模式系列】5.装饰器模式和适配器模式
  8. 唐相儒计算机,2016华中师范大学自主招生计算机类入围名单
  9. 图神经网络应用——基于深度学习的图相似度计算(以SIMGNN为例的保姆级讲解)
  10. win7计算机出现空白图标,Win7程序图标变空白显示不全的解决方法