图片裁剪功能集成优化
问题所在
最近公司编辑在发布新闻的时候遇到一个问题,编辑后台提供的原有的图片裁剪功能在移动端和一些特定类型的显示时达不到具体的要求。最后去深究发现我们的服务器对上传上来的图片进行了一次裁剪,为了减小图片大小对它进行了不同比例,不同尺寸的裁剪。但这种裁剪会丢失一些图片上的信息,对于我们这种信息传递的公司肯定是不希望的。如何解决,有如下方案:
- 让编辑学会Photoshop自行编辑图片上传(让每个编辑改编自己的工作习惯不现实)
- 我们开发一个简单、易用的在线图片编辑功能(能用功能解决的都不是问题)
所以基于上面所说的这些,这个功能放在后端显然不现实。为了一个小的功能改变现有的业务逻辑,所以就想放在前端先把图片裁剪好,然后上传服务器。服务器的裁剪后最后选择最全的那一张,显然服务器现在也是这么做的。
图片裁剪功能
首先想到的是有没有这样的插件可以使用,幸运的是有这样的一个优秀插件可以使用cropper.js.翻看了它的文档,发现也是很好使用的。
<!-- Wrap the image or canvas element with a block element (container) -->
<div>
<img id="image" src="picture.jpg">
</div>
$('#image').cropper({
aspectRatio: 16 / 9,
crop: function(e) {
// Output the result data for cropping image.
console.log(e.x);
console.log(e.y);
console.log(e.width);
console.log(e.height);
console.log(e.rotate);
console.log(e.scaleX);
console.log(e.scaleY);
}
});
但是细细研究了下发现它是使用了canvas进行的图片裁剪,所以只有在现代浏览器才能支持。咨询了下需求,庆幸的是我们的系统不考虑兼容性。那就好办了,既然已有了好的轮子为什么不用。
之前我们的图片上传是这样的,非常简陋。就根本是个按钮上传图片,没有什么交互体验。
图片按钮如下,
选择图片后如下,
这样的肯定是不行的,必须首先将图片呈现出来。但是javascript操作在浏览器环境中读取文件肯定是很困难的。window.URL.createObjectURL() 静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的URL。这个 URL 的生命周期和创建它的窗口中的 document 绑定。这个新的URL 对象表示指定的 File 对象或 Blob 对象。这样就可以对本地上传图片进行操作了。
//local image change
function setImagePreview(avalue) {
var docObj=document.getElementById("doc"),
imgObjPreview=document.getElementById("image"),
img_src;
if(docObj.files[0].size/1024/1024 > 1.2) {
alert('请选择小于1.2M图片');
return false;
}
if(docObj.files &&docObj.files[0]) {
//火狐7以上版本不能用上面的getAsDataURL()方式获取,需要一下方式
img_src = window.URL.createObjectURL(docObj.files[0]);
imgObjPreview.src = img_src;
} else {
//IE下,使用滤镜
docObj.select();
var imgSrc = document.selection.createRange().text;
var localImagId = document.getElementById("localImag");
//必须设置初始大小
localImagId.style.width = "150px";
localImagId.style.height = "180px";
//图片异常的捕捉,防止用户修改后缀来伪造图片
try {
localImagId.style.filter="progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale)";
localImagId.filters.item("DXImageTransform.Microsoft.AlphaImageLoader").src = imgSrc;
img_src = imgSrc;
} catch(e) {
alert("您上传的图片格式不正确,请重新选择!");
return false;
}
imgObjPreview.style.display = 'none';
document.selection.empty();
}
return img_src;
}
选择图片后引入cropper.js,效果如下
代码如下:
/*init cropper*/
function cropperContainer() {
var cropperContainer = $('#image').cropper({
aspectRatio: 4/3,
viewMode: 1,
autoCropArea: 1,
cropBoxResizable:true
});
}
//init cropper
cropperContainer();
$('#cropper').on('click', function() {
var croppedCanvas,
roundedCanvas;
/*if (!croppable) {
return false;
}*/
$('.cropper-text').show();
// true
croppedCanvas = $('#image').cropper('getCroppedCanvas');
// Round
roundedCanvas = getRoundedCanvas(croppedCanvas);
//true
var base64 = roundedCanvas.toDataURL();
// var imgData = base64.split(',')[1];
// imgData = window.atob(imgData);
// var resultData = new Uint8Array(imgData.length);
// for(var i = 0; i < imgData.length; i++) {
// resultData[i] = imgData.charCodeAt(i);
// }
// var blob = new Blob([resultData], {type:'image/png'});
// Show
$('#results').html('<img src="' + base64 + '">');
$('#save-btn').click(function() {
window.opener._getImageObj(base64);
window.close();
});
});
最终得到的是一个base64图片,通过ajax提交给服务器似乎会因为base64太大提交不成功。然后我做了个很傻的事情,将base64转化为blob转化为文件
//base64 to blob then to file
function to_blob(base64) {
var imgData = base64.split(',')[1];
imgData = window.atob(imgData);
var resultData = new Uint8Array(imgData.length);
for(var i = 0; i < imgData.length; i++) {
resultData[i] = imgData.charCodeAt(i);
}
//convert
var blob = new Blob([resultData], {type:'image/png'});
var file = new File([blob], 'upload_'+Date.now()+'.png', {type: 'image/png', lastModified: Date.now()});
return file;
}
最后又将整个表单通过FormData对象转化为javascript对象提交到后台。
裁剪后如图,
总结
其实在我说来似乎没什么技术含量,但是在具体的生产环境中将插件集成上去也是遇到了很多麻烦。比如当处理跨域的用document.domain来处理,并且要在已成型的系统中迎合原有的后台架构。后端的支持极其的少,所以几乎是去了解后端的业务逻辑,然后完成的功能。