題記
我想大家對頭像上傳功能在陌生不過了把,每個人都應該用過,當然,從不更改頭像的大俠除外,寫到這里,我突然想到我的CSDN用戶也沒有頭像,於是,我到設置里面准備去看看CSDN的頭像上傳功能是怎樣的,可是我弄了半天都沒搞定,提示說高版本瀏覽器不支持預覽,難道只有IE6才支持?難得吐槽一下,不知道是我人品問題還是沒用對。如圖:
准備工作
好了,廢話不多說了,一般常用的頭像上傳有兩種(據我所知):
普通的文件上傳:
普通文件上傳莫非就是,給你一個選擇文件選擇按鈕,你選擇好文件后,剩下的事情你就不管了,后台會把你上傳的圖片按一定的比例縮放,或者無任何處理,或其他操作,這種方式的好處壞處顯而易見,前后台的實現相對來說比較簡單,可是用戶體驗就不是這么滿意了,我以前也遇到過這種,不但自己需要找到大概合適大小比例的圖片,有事還得自己修改裁剪,然后再傳上去,最后結果並不會讓我滿意,我不得不重新在去做一遍這些繁瑣的操作!
帶預覽裁剪功能的上傳:
現在很多用戶體驗還不錯的網站或軟件都采用這種方法,最常用的實現就是用的flash,用戶上傳一張圖片,馬上就可以自己選取需要的部分,而且還帶有及時的預覽效果,體驗非常不錯。
步入正題,最近項目需要,有個地方需要用戶上傳頭像,本來按照產品設計,直接一個選擇框,用戶選擇圖片后上傳就搞定!這種方法不說用戶,就連我開發人員都覺得真心難用,於是我決定弄一個可以預覽裁剪上傳頭像的功能,下面是用到的一款js的圖片裁剪插件,性能還算行幾乎兼容所有的瀏覽器。
實現原理
當我沒接觸到這個功能之前,以為這有多神秘多神秘,其實就是兩個原圖的img標簽,其中一個是讓你選擇預覽范圍,一個讓你看到你選擇后的效果,利用Draggable和Resizable特性在原圖上創建出一個選擇框,然后再按照你選擇的范圍按照一定的比例和算法讓效果圖那個img只顯示你選擇的那一部分,最后把你最后選擇的范圍坐標以及大小傳給后台,后台按照這個范圍裁剪相應的圖片即可!
具體用法
首先引入相應的js/css文件
然后寫入一個image標簽,
最后給img標簽添加可裁剪功能
jQuery(function($) {
$('#target').Jcrop();
});
效果如下:
如果想預覽你選擇后的圖片,再寫入一個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();
}
}