Jcrop是一款优秀的jQuery插件,可以非常方便地实现图像裁剪,而且功能十分的强大。
一般的情况下,图像裁剪的实现要经过两次图像上传,第一次将图片上传到后台,后台返回一个链接,通过这个链接在本地实现预览。第二次将图片本身以及裁剪参数上传到后台,后台进行裁剪,并保存在服务器。也就是说第一次的上传是不必要的,用户万一中途取消了操作,那第一次的操作就完全成了无用操作。而且增加了网络的消耗,造成了不必要的浪费。一般这种情况下,我们会考虑第二次不进行上传而使用第一次上传的图片,但这增加了技术难度,而且造成了更多的Exceptions的可能。事实
上有更好的选择,那就是FileAPI,它不进行上传,图片的预览是通过将图片加载到本地浏览器缓存实现的。我们通过FileAPI获取必要的参数,在用户“下定决心”使用该图的时候再进行上传,可以极大的降低多余消耗,增强用户体验。
要实现图像裁剪上传,首先要实现图像本地预览的功能。定义一个priviewImage.js文件,方便复用:
function previewImage(file, callback) { /* * file:file控件 prvid: 图片预览容器 */ /*if (file[0].fileSize() > 3 * FileAPI.MB) { alert("The uploading file size must less than 3MB!"); return; }*/ var tip = "Expect jpg or png or gif!"; // 设定提示信息 var filters = { "jpeg" : "/9j/4", "gif" : "R0lGOD", "png" : "iVBORw" } if (window.FileReader) { // html5方案 for (var i = 0, f; f = file.files[i]; i++) { var fr = new FileReader(); fr.onload = function(e) { var src = e.target.result; if (!validateImg(src)) { alert(tip); } else { showPrvImg(src); } } fr.readAsDataURL(f); } } else { // 降级处理 if (!/\.jpg$|\.png$|\.gif$/i.test(file.value)) { alert(tip); } else { showPrvImg(file.value); } } function validateImg(data) { var pos = data.indexOf(",") + 1; for ( var e in filters) { if (data.indexOf(filters[e]) === pos) { return e; } } return null; } function showPrvImg(src) { callback(src); } }
通过callback可以拿到字节码形式的图片,将其设为img标签的src属性,浏览器即可以显示出来。以下是一个例子:
<div> <p>预览:</p> <div id="previewBox"> <img src="" id="previewImage" alt="预览头像"> </div> </div>
previewImage(file, function(src) { $("#previewImage").attr("src", src);}
现在来描述下Jcrop图像裁剪上传的基本流程:
第一步:用户点击fileinput,选择本地图片,并通过FileAPI将该文件加载到浏览器缓存,最后形成网页上的本地预览图片;
第二步:Jcrop针对预览图片进行初始化,关于初始化的具体方法,我会在后面说到。这里有一个难题,事实上解决方案是一个选择器的问题,后面详细说明,关于Jcrop的详细使用方法,可以自行百度,网上有很多的相关介绍;
第三步:通过Jcrop将用户的裁剪数据捕捉到hiddeninput中,这个便是Jcrop的核心,所以不需要我们做过多的考虑;
第四步:用户决定是应用操作还是放弃操作。若放弃,则将DOM重置,若应用,则用ajaxFileUpload上传图片。
现在来详细说明各步骤的实现:
首先是文件的加载,需要的依赖有jQuery.js,Jcrop.js,ajaxFileUpload.js以及我们先前定义的previewImage.js:
<link rel="stylesheet" href="css/jquery.Jcrop.min.css" type="text/css"> <script type="text/javascript" src="js/jquery-1.10.2.min.js"></script> <script type="text/javascript" src="js/jquery.Jcrop.min.js"></script> <script type="text/javascript" src="js/previewImage.js"></script> <script type="text/javascript" src="js/ajaxfileupload.js"></script>
基本的DOM模型:
<div> <input id="myImage" name="myImage" type="file" onchange="initImgForm(this)"> <button type="button" id="resetImgBtn">取消选择</button> <button type="button" id="imgBtn">确认修改</button> <input type="hidden" id="offsetX" name="offsetX" value="0"> <input type="hidden" id="offsetY" name="offsetY" value="0"> <input type="hidden" id="width" name="width" value="120"> <input type="hidden" id="height" name="height" value="120"> </div>
说到这里,我多嘴一句,button元素在使用的时候最好指定一下type="button",因为它在和form元素组合使用的时候,若不指定属性,在某些浏览器中会触发form的submit事件,造成一些不必要的错误。DOM还有一部分,预览窗格:
<div> <p>预览:</p> <div id="previewBox"> <img src="" id="previewImage" alt="预览头像"> </div> </div>
当用户点击fileinput的时候,将会调用initImgForm方法,完成图片预览以及Jcrop的初始化。Jcrop出事化的关键在于$.children()方法,直接针对父元素"#previewImage"会出现错误,同时也不可对其子元素<img>调用$.Jcrop方法,也会出现错误。
<script type="text/javascript"> var JcropApi, boundx, boundy; function initImgForm(file) { /* 验证文件大小还未实现,服务器端最大Size 3MB,参阅 previewImage.js*/ previewImage(file, function(src) { $("#previewImage").attr("src", src); $("#previewBox").children().Jcrop({ onChange : showPreview, onSelect : showPreview, minSize : [ 120, 120 ], maxSize : [ 400, 400 ], aspectRatio : 1 }, function() { var bounds = this.getBounds(); boundx = bounds[0]; boundy = bounds[1]; JcropApi = this; }); }); } function showPreview(c) { var w = $("#previewImage").width(); var h = $("#previewImage").height(); var rx = w / c.w; var ry = h / c.h; $("#previewImage").css({ width : Math.round(rx * w) + "px", height : Math.round(ry * h) + "px", marginLeft : "-" + Math.round(rx * c.x) + "px", matginTop : "-" + Math.round(ry * c.y) + "py" }); $("#offsetX").val(c.x); $("#offsetY").val(c.y); $("#width").val(c.w); $("#height").val(c.h); } $("#resetImgBtn").on("click", function() { resetImg(); }); $("#imgBtn").on("click", function() { var params = {}; params.offsetX = $("#offsetX").val(); params.offsetY = $("#offsetY").val(); params.width = $("#width").val(); params.height = $("#height").val(); $.ajaxFileUpload({ url : "...",//定义你的上传路径 secureurl : false, fileElementId : "myImage", data : params, dataType : "json", success : function(json) { if (json.status) { $("#currentImage").attr("src", json.imgsrc); resetImg(); alert(json.message); } else { alert(json.message); } }, error : function() { alert("请求失败"); } }); }); function resetImg() { $("#previewBox").empty(); var img = "<img id=\"previewImage\" src=\"\" alt=\"预览头像\">"; $("#previewBox").append(img); $("#myImage").val(""); JcropApi.destroy(); } </script>