题记
我想大家对头像上传功能在陌生不过了把,每个人都应该用过,当然,从不更改头像的大侠除外,写到这里,我突然想到我的CSDN用户也没有头像,于是,我到设置里面准备去看看CSDN的头像上传功能是怎样的,可是我弄了半天都没搞定,提示说高版本浏览器不支持预览,难道只有IE6才支持?难得吐槽一下,不知道是我人品问题还是没用对。如图:
准备工作
好了,废话不多说了,一般常用的头像上传有两种(据我所知):
普通的文件上传:
普通文件上传莫非就是,给你一个选择文件选择按钮,你选择好文件后,剩下的事情你就不管了,后台会把你上传的图片按一定的比例缩放,或者无任何处理,或其他操作,这种方式的好处坏处显而易见,前后台的实现相对来说比较简单,可是用户体验就不是这么满意了,我以前也遇到过这种,不但自己需要找到大概合适大小比例的图片,有事还得自己修改裁剪,然后再传上去,最后结果并不会让我满意,我不得不重新在去做一遍这些繁琐的操作!
带预览裁剪功能的上传:
现在很多用户体验还不错的网站或软件都采用这种方法,最常用的实现就是用的flash,用户上传一张图片,马上就可以自己选取需要的部分,而且还带有及时的预览效果,体验非常不错。
步入正题,最近项目需要,有个地方需要用户上传头像,本来按照产品设计,直接一个选择框,用户选择图片后上传就搞定!这种方法不说用户,就连我开发人员都觉得真心难用,于是我决定弄一个可以预览裁剪上传头像的功能,下面是用到的一款js的图片裁剪插件,性能还算行几乎兼容所有的浏览器。
Jcrop http://deepliquid.com/content/Jcrop.html
实现原理
当我没接触到这个功能之前,以为这有多神秘多神秘,其实就是两个原图的img标签,其中一个是让你选择预览范围,一个让你看到你选择后的效果,利用Draggable和Resizable特性在原图上创建出一个选择框,然后再按照你选择的范围按照一定的比例和算法让效果图那个img只显示你选择的那一部分,最后把你最后选择的范围坐标以及大小传给后台,后台按照这个范围裁剪相应的图片即可!
具体用法
首先引入相应的js/css文件
然后写入一个image标签,
如果想预览你选择后的图片,再写入一个img标签
$(function(){
$('#target').Jcrop({
onChange: showPreview,
onSelect: showPreview,
aspectRatio: 1
});
});
当选择框改变,将会执行下面的函数
function showPreview(coords)
{
var rx = 100 / coords.w;
var ry = 100 / coords.h;
$('#preview').css({
width: Math.round(rx * 300) + 'px',
height: Math.round(ry * 300) + 'px',
marginLeft: '-' + Math.round(rx * coords.x) + 'px',
marginTop: '-' + Math.round(ry * coords.y) + 'px'
});
}
这样,id为preview将只会显示你选择的范围的图片
与JAVA后台结合
前台实现其实很简单,Jcrop下载下来已经有完整的Demo可以运行,用法也很简单,最麻烦的就是后台了,我们可以通过回调函数showPreview得到选择框的x,y,width,height,然后传到后台按照坐标裁剪图片即可,官方是这种说的,可问题来了,我们得到的x,y,w,h只是相对于被缩小后的id为target的img标签的,假设一张1024x768的图,target的大小肯定是固定的,图中为504x374,所以得到的x,y,h,w是基于此比例来的,如果传到后台你用这个坐标来裁剪原图,肯定有问题,这个问题纠结了我很久,我想过通过一定的比例算法来计算实际的x,y是多少,但一直都没找到传到后台的坐标和实际要裁剪的坐标有什么关系,我网上查了很多人说也是直接通过传过来的x,y等参数直接生成新的图片,可生成的图片完全和我选择的不一样,我也想过把图片先压缩成target一样大,然后xy就正确了,但是看到preview里的效果图了吗,那是在原图上选择后的效果,后来我发现preview不就是我要的效果吗,于是打开firebug研究了一下
通过样式我们看到,图片被放大到758x561,并且左外边距和上外边距分别设置成了-529和-261,于是我明白了,后台按照一样的思路不就行了吗?先把图片转成758x561,然后在按照x:528 y:261的坐标裁剪一定的大小,不就是最后要的图片么,
后台用到的java代码方法
/**
* 缩放后裁剪图片方法
* @param srcImageFile 源文件
* @param x x坐标
* @param y y坐标
* @param destWidth 最终生成的图片宽
* @param destHeight 最终生成的图片高
* @param finalWidth 缩放宽度
* @param finalHeight 缩放高度
*/
public static void abscut(String srcImageFile, int x, int y, int destWidth,
int destHeight,int finalWidth,int finalHeight) {
try {
Image img;
ImageFilter cropFilter;
// 读取源图像
BufferedImage bi = ImageIO.read(new File(srcImageFile));
int srcWidth = bi.getWidth(); // 源图宽度
int srcHeight = bi.getHeight(); // 源图高度
if (srcWidth >= destWidth && srcHeight >= destHeight) {
Image image = bi.getScaledInstance(finalWidth, finalHeight,Image.SCALE_DEFAULT);//获取缩放后的图片大小
cropFilter = new CropImageFilter(x, y, destWidth, destHeight);
img = Toolkit.getDefaultToolkit().createImage(
new FilteredImageSource(image.getSource(), cropFilter));
BufferedImage tag = new BufferedImage(destWidth, destHeight,
BufferedImage.TYPE_INT_RGB);
Graphics g = tag.getGraphics();
g.drawImage(img, 0, 0, null); // 绘制截取后的图
g.dispose();
// 输出为文件
ImageIO.write(tag, "JPEG", new File(srcImageFile));
}
} catch (Exception e) {
e.printStackTrace();
}
}