基于Jcrop做客户端无上传图片剪裁

来源:互联网 发布:丹东917事件知乎 编辑:程序博客网 时间:2024/06/05 17:56

首先要说,jcrop做剪裁,是很强大的,也有很多实现了。不过,对于这些实现,都有个缺点,无法做到不经上传的客户端实时预览与剪裁效果预览(我是指兼容浏览器)。主要原因就是ie太蛋疼了。为了更好地挖掘客户端的性能,让服务器压力更小一些,我只好研究一下了。


惯例,先说原理:

1、jcrop做图片选区。

2、ie基于滤镜做预览,其他浏览器基本是设置src,但获取图片源的方式略有不同。

3、利用overflow:hidden和margin负值做剪裁效果预览。

4、实际上客户端未做文件剪裁,但能得到响应剪裁信息,在服务端处理时剪裁。

5、ie基于滤镜做预览时,使用透明图片覆盖在div上方做jcrop的基础(jcrop要基于img)。

6、由于涉及覆盖,jcrop生成的背景div带色,需要设置该颜色为透明色。


上效果……

chrome下的效果:


ie下的效果:



ok,我还是懒人一个,看代码:


照例先上js再上css,额,这次有个透明image,比较蛋疼。

先看引用吧:

<link rel="stylesheet" type="text/css" href="css/image.crop.css"/><link rel="stylesheet" type="text/css" href="css/jquery.Jcrop.min.css"/><script type="text/javascript" src="js/jquery-1.11.1.min.js"></script><script type="text/javascript" src="js/jquery.Jcrop.min.js"></script><script type="text/javascript" src="js/jquery.imagecrop.croper.js"></script><script type="text/javascript" src="js/jquery.imagecrop.previewer.js"></script>
第一个css是实现方案,的引用样式。

第二个css是jcrop的引用样式。

jquery不必说了……

jcrop的引用库

后面两个js就是本次实现的关键,croper.js是在jcrop的基础上封装一层。

previewer.js就是实现预览的关键。


上js了……

先是croper.js:

if(typeof(jquery) == "undefined"){jquery = {};}if(typeof(jquery.imagecrop) == "undefined"){jquery.imagecrop = {};}/** * 截图工具 * @param opts 配置 */jquery.imagecrop.croper = function(opts){var configs = opts;var me = this;var jcrop;var select;var bounds;this.getCrop = function(){return jcrop;}this.tellSelect = function(){return select;}this.getBounds = function(){return bounds;}/** * 执行选区预览 * @param select * @param bounds */this.doPreview = function(select,bounds){var realWidth = bounds[0];var realHeight =  bounds[1];var previewContainerWidth = configs.previewContainer.width();var previewImgWidth = (configs.previewContainer.width() * realWidth) / select.w;configs.previewImg.attr("src", configs.orgImg.attr("src"));configs.previewImg.width(previewImgWidth);var marginLeft = (-1) * select.x * configs.previewImg.width() / realWidth;var marginTop = (-1) * select.y *  configs.previewImg.height() / realHeight;configs.previewImg.css("marginLeft", marginLeft);configs.previewImg.css("marginTop", marginTop);configs.previewImg.show();};/** * 执行选取 * @param settings jcrop配置参数 */this.crop = function(settings){var cropSettings = {aspectRatio : configs.previewContainer.width() / configs.previewContainer.height(),onChange : function(){select = this.tellSelect();bounds = this.getBounds();me.doPreview(select,bounds);}};if(settings){$.extend(cropSettings,settings);}//jcrop = configs.orgImg.Jcrop(cropSettings);jcrop = $.Jcrop("#" + configs.orgImg.attr("id"), cropSettings);}/** * 销毁 */this.desdroy = function(){if(jcrop && jcrop.destroy){jcrop.destroy();}}}
这个比较简单,主要是定义了默认的预览事件,兼容ie外的浏览器。

然后是previewer.js:

if(typeof(jquery) == "undefined"){jquery = {};}if(typeof(jquery.imagecrop) == "undefined"){jquery.imagecrop = {};}/** * 预览器 * @param file 用于测试是否支持html5 */jquery.imagecrop.previewer = function(file){var browserVersion = window.navigator.userAgent.toUpperCase();var jcroper;var me = this;var supportHTML5 = false;var html =  '<span class="image-crop-container">' +'<fieldset class="image-crop-fieldset">' +'<legend>原图</legend>' +'<div class="image-crop-org-container" id="image_crop_org_container">' +'<div id="image_crop_org_div" class="image-crop-org-div"></div>' +'<img id="image_crop_org_img" class="image-crop-org-img" src="images/touming.gif"/>' +'</div>' +'</fieldset>' +'<div class="image-crop-container">' +'<fieldset class="image-crop-fieldset simple-div">' +'<legend>缩略图</legend>' +'<div class="image-crop-preview-container" id="image_crop_preview_container">' +'<div id="image_crop_preview_div"></div>' +'<img id="image_crop_preview_img" src="images/touming.gif"/>' +'</div>' +'</fieldset>' +'</div>' +'</span>';/** * 判断是否数组 * @param obj 参数对象 * @returns boolean */var isArray = function (obj) {return Object.prototype.toString.call(obj) === '[object Array]';}/** * 继承 * @param a 基础对象 * @param b 为object时,继承其所有属性,为array时,继承其所有成员的所有属性 * @return 组成对象 */var extend = function(a, b){if(a){if(isArray(b)){for(var i in b){var o = b[i];for(var p in o){a[p] = o[p];}}}else if(typeof(b) == "object"){for(var p in b){a[p] = b[p];}}return a;}}/** * 默认配置 */var defaultConfig = {jcropBaseClass : "jcrop",fileChooser : file};/** * 获取jcrop对象 * @returns jcrop */this.getCrop = function(){if(!jcroper){return undefined;}else{return jcroper.getCrop();}}/** * 获取选框的值(实际尺寸) * @returns select{x,y,x2,y2,w,h} */this.tellSelect = function(){if(!jcroper){return undefined;}else{return jcroper.tellSelect();}}/** * 获取选框的值(界面尺寸) * @returns select{x,y,x2,y2,w,h} */this.tellScaled = function(){if(!jcroper){return undefined;}else{return jcroper.getCrop().tellScaled();}}/** * 获取图片实际尺寸 * @returns [w, h] */this.getBounds = function(){if(!jcroper){return undefined;}else{return jcroper.getBounds();}}/** * 获取图片显示尺寸 * @returns [w, h] */this.getWidgetSize = function(){if(!jcroper){return undefined;}else{return jcroper.getCrop().getWidgetSize();}}/** * 获取图片缩放的比例 * @returns [w, h] */this.getScaleFactor = function(){if(!jcroper){return undefined;}else{return jcroper.getCrop().getScaleFactor();}}/** * 初始化 * @param container * @param emptyImage * @param file */this.init = function(container, emptyImage){$(container).html(html);var c = {enterBtn : $("#image_crop_enter_btn"),cancelBtn : $("#image_crop_cancel_btn"),orgContainer : $("#image_crop_org_container"),orgDiv : $("#image_crop_org_div"),orgDivClass : "image-crop-org-div",orgImg : $("#image_crop_org_img"),orgImgClass : "image-crop-org-img",previewContainer : $("#image_crop_preview_container"),previewDiv : $("#image_crop_preview_div"),previewImg : $("#image_crop_preview_img"),emtpyImage : emptyImage};/** * 继承配置 */this.configs = $.extend(defaultConfig , c);jcroper = new jquery.imagecrop.croper(this.configs);}/** * 销毁,在预览前调用,清除上一次预览结果 */this.desdroy = function(){if(typeof(this.configs) == "undefined"){return;}var parent = this.configs.orgDiv.parent();//----------reset org div----------this.configs.orgDiv.remove();var divId = "image_crop_org_div_" + new Date().getTime();var divDom = document.createElement("div");divDom.id = divId;parent.append($(divDom));this.configs.orgDiv = $("#" + divId);this.configs.orgDiv.addClass(this.configs.orgDivClass);//----------reset org div----------//----------reset org img----------this.configs.orgImg.remove();var imgId = "image_crop_org_img_" + new Date().getTime();var imgDom = document.createElement("img");imgDom.id = imgId;parent.append($(imgDom));this.configs.orgImg = $("#" + imgId);this.configs.orgImg.addClass(this.configs.orgImgClass);//----------reset org img----------//----------reset preview div----------//通过滤镜实现缩略图预览this.configs.previewDiv.css("filter","progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='scale',src='" + this.configs.emptyImage + "')");//----------reset preview div----------//----------reset preview img----------this.configs.previewImg.attr("src", this.configs.emtpyImage);//----------reset preview img----------}/** * 预览图片 * @param file 选择文件的input对象 */this.preview = function(file){//设置预览this.configs.orgImg.height("auto");this.configs.orgImg.attr("src", file.value);//级联变动this.configs.orgDiv.hide();this.configs.previewDiv.hide();this.configs.orgImg.show();this.configs.previewImg.show();//初始化截图工具this.doCrop(file.value);}/** * 对预览图片进行截图初始化 * @param src 图片源 * @param settings 可选参数,用于指定jcrop参数,但需在指定自定义doPreview时才其作用 * @param doPreview 可选参数,自定义doPreview,用于jcrop回调,方法入参:select,bounds。参见jcrop。 */this.doCrop = function(src, settings, doPreview){//如果当前已存在jcroper,则先销毁重建if(jcroper){jcroper.desdroy();}$("." + this.configs.jcropBaseClass + "-holder").remove();jcroper = new jquery.imagecrop.croper(this.configs);//DIV预览实现if(doPreview){jcroper.doPreview = doPreview;jcroper.crop(settings);//IMG预览实现}else{jcroper.crop();}}if(file.files){supportHTML5 = true;}else{supportHTML5 = false;}//进行浏览器判断,选择对应的实现方式if(supportHTML5){return extend(this, new jquery.imagecrop.previewer.impls("html5Preview"));} else if (browserVersion.indexOf("MSIE") > -1 && browserVersion.indexOf("MSIE 6") > -1) {return extend(this, new jquery.imagecrop.previewer.impls("ie6Preview"));} else if (browserVersion.indexOf("MSIE") > -1 && browserVersion.indexOf("MSIE 6") <= -1) {return extend(this, new jquery.imagecrop.previewer.impls("ie7to9Preview"));} else if (browserVersion.indexOf("FIREFOX") > -1) {return extend(this, new jquery.imagecrop.previewer.impls("firefoxPreview"));} else{return this;}}/** * 不同浏览器的实现方式 * @param name 实现方式名称 */jquery.imagecrop.previewer.impls = function(name){var impls = {/** * ie6实现 */ie6Preview : function(){var me = this;this.preview = function(file){//设置预览this.configs.orgImg.height("auto");this.configs.orgImg.attr("src", file.value);//级联变动this.configs.orgDiv.hide();this.configs.previewDiv.hide();this.configs.orgImg.show();this.configs.previewImg.show();//初始化截图工具this.doCrop(file.value);}return this;},/** * ie7+实现 */ie7to9Preview : function(){var me = this;/** * 获取图片的实际大小 * @param tmpSrc 图片源 * @return {w,h} 宽和高 */this.getImageBounds = function(tmpSrc){var imgObj = new Image();imgObj.src = tmpSrc;var width = imgObj.width;var height = imgObj.height;if((typeof width=="undefined" || width==0) && (typeof height=="undefined" || height==0)){var picpreview=document.getElementById("image_crop_org_container");    var tempDiv=document.createElement("div");    picpreview.appendChild(tempDiv);    tempDiv.style.width="10px";    tempDiv.style.height="10px";    tempDiv.style.diplay="none";    tempDiv.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='image',src='" + tmpSrc + "');";    tempDiv.ID="previewTemp" + new Date().getTime();    width=tempDiv.offsetWidth;    height=tempDiv.offsetHeight;    picpreview.removeChild(tempDiv);}var w = me.configs.orgContainer.width();var h = height * w / width;return {w:w,h:h};};/** * ie7+为div预览,需要实现自定义doPreview * @param select 选区 * @param bounds 实际内容 */this.doPreview = function(select,bounds){var realWidth = bounds[0];var realHeight =  bounds[1];//计算预览大小var previewContainerWidth = me.configs.previewContainer.width();var previewDivWidth = (me.configs.previewContainer.width() * realWidth) / select.w;var previewDivHeight = (me.configs.previewContainer.height() * realHeight) / select.h;//通过滤镜实现缩略图预览me.configs.previewDiv.css("filter","progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='scale',src='" + me.configs.orgImg.attr("realSrc") + "')");//设定预览大小me.configs.previewDiv.height(previewDivHeight);me.configs.previewDiv.width(previewDivWidth);//计算偏移var marginLeft = (-1) * select.x * me.configs.previewDiv.width() / realWidth;var marginTop = (-1) * select.y *  me.configs.previewDiv.height() / realHeight;//设置偏移实现剪切me.configs.previewDiv.css("marginLeft", marginLeft);me.configs.previewDiv.css("marginTop", marginTop);me.configs.previewDiv.show();me.configs.orgImg.show();}this.preview = function(file){me = this;file.select();var browserVersion = window.navigator.userAgent.toUpperCase();//如果是ie9需要触发blur事件,避免被安全规则阻拦if (browserVersion.indexOf("MSIE 9") > -1){file.blur();// 不加上document.selection.createRange().text在ie9会拒绝访问}var tmpSrc = document.selection.createRange().text;//设置原图预览滤镜this.configs.orgDiv.css("filter","progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='scale',src='" + tmpSrc + "')");//获取图片实际大小(由于使用缩放滤镜scale必须指定div宽高,无法做到自适应,因此先通过image滤镜获取实际大小)var bounds = this.getImageBounds(tmpSrc);//级联变动this.configs.orgDiv.width(bounds.w);this.configs.orgDiv.height(bounds.h);this.configs.orgDiv.show();//需要对透明的img使用绝对定位并设置offset为预览div的offset,避免jcrop无法覆盖在预览div上面this.configs.orgImg.css("position","absolute");this.configs.orgImg.css("display","inline-block");this.configs.orgImg.offset(this.configs.orgDiv.offset());this.configs.orgImg.width(bounds.w);this.configs.orgImg.height(bounds.h);this.configs.orgImg.attr("src", this.configs.emtpyImage);this.configs.orgImg.attr("realSrc", tmpSrc);this.configs.orgImg.show();this.configs.previewDiv.show();this.configs.previewImg.hide();//初始化截图工具this.doCrop(this.configs.emtpyImage,{bgColor : "#00000000" //DIV预览需要遮盖物透明},this.doPreview);}return this;},/** * 火狐浏览器实现 */firefoxPreview : function(){this.preview = function(file){var browserVersion = window.navigator.userAgent.toUpperCase();var firefoxVersion = parseFloat(browserVersion.toLowerCase().match(/firefox\/([\d.]+)/)[1]);var src;// firefox7以下版本if (firefoxVersion < 7) {src = file.files[0].getAsDataURL();// firefox7.0+} else {src = window.URL.createObjectURL(file.files[0]);}//设置预览this.configs.orgImg.height("auto");this.configs.orgImg.attr("src", src);//级联变动this.configs.orgDiv.hide();this.configs.previewDiv.hide();this.configs.orgImg.show();this.configs.previewImg.show();//初始化截图工具this.doCrop(src);}return this;},/** * html5实现 */html5Preview : function(){this.preview = function(file){var me = this;var browserVersion = window.navigator.userAgent.toUpperCase();if (window.FileReader) {var reader = new FileReader();reader.onload = function(e) {me.configs.orgImg.height("auto");me.configs.orgImg.width(me.configs.orgContainer.width());me.configs.orgImg.attr("src", e.target.result);me.configs.orgImg.show();//设置预览//级联变动me.configs.orgDiv.hide();me.configs.previewDiv.hide();me.configs.orgImg.show();me.configs.previewImg.show();//初始化截图工具me.doCrop(e.target.result);}reader.readAsDataURL(file.files[0]);} else if (browserVersion.indexOf("SAFARI") > -1) {alert("不支持Safari6.0以下浏览器的图片预览!");}}return this;},/** * 默认实现 */defaultPreview : function(){this.preview = function(file){//设置预览this.configs.orgImg.height("auto");this.configs.orgImg.attr("src", file.value);//级联变动this.configs.orgDiv.hide();this.configs.previewDiv.hide();this.configs.orgImg.show();this.configs.previewImg.show();//初始化截图工具this.doCrop(file.value);}return this;}}return impls[name]();}
这个就比较蛋疼了,涉及了多个浏览器的具体预览实现方案。具体……看注释吧。

调用代码:

$(document).ready(function(){var file = document.getElementById("image_crop_file_chooser");var previewer = new jquery.imagecrop.previewer(file);previewer.init("#previewer", "images/touming.gif");$(file).change(function(){previewer.desdroy();previewer.preview(file);});$("#image_crop_enter_btn").click(function(){console.log(previewer.getCrop());});});



最后,上css……

/*页面主体*/body {text-align: center;margin: 0px;}/*列布局容器*/.image-crop-container {display: inline-block;}/*区域*/.image-crop-fieldset{border: 1px solid #dedede;float: left;display: inline-block;}/*区域标题*/.image-crop-fieldset legend {font-size: 13px;}/*原图区域容器*/.image-crop-org-container{width: 600px;overflow: hidden;margin: 5px;}/*原图预览DIV*/.image-crop-org-div{width: 100%;float: left;display: inline-block;}/*原图预览IMG*/.image-crop-org-img{width: 600px;float: left;display: inline-block;}/*截图预览区域容器*/.image-crop-preview-container{float: left;display: inline-block;overflow: hidden;margin: 5px;width: 300px;/*由此决定宽高比*/height: 200px;/*由此决定宽高比*/}/*截图预览DIV*/#image_crop_preview_div{width: 100%;float: left;display: none;/*开始不显示*/}/*截图预览IMG*/#image_crop_preview_img{width: 100%;float: left;display: none;/*开始不显示*/}/*普通DIV*/.simple-div {display: block;float: none;}/*文件选择*/#image_crop_file_chooser{margin: 5px;}/*按钮容器*/.buttonS {margin: 5px;}/*按钮*/.buttonS input {width: 80px;padding: 5px;border: 1px solid #efefef;background-color: #efefef;cursor: pointer;}/*按钮聚焦*/.buttonS input:HOVER {border: 1px solid #bdbdbd;background-color: #bdbdbd;}
这个不是一定的,是可以调整的,各页面可更改,可自定义设置,但名称是约定的。不过inline-block、float:left和hidden可别改了……


额,其实最关键的是处理ie那段,欺骗jcrop,给张假图然它选,实际预览处理改取真图,囧,然后就是很蛋疼的滤镜和必须指定宽高……


over。






0 0