序言:
上次發了幾個jQuery的外掛程式和些心得, 很多園友(也許是自己站上的)發郵件希望能提供更多的源碼, 正好這次有個同學希望在自己的罈子上掛個圖片裁剪以產生頭像的功能, 於是幫忙寫了這麼個外掛程式.也許很多園友用得著, 就一併發上來啦.
本文:
為了使層次分明及便於閱讀, 整個解決方案如下:
其中BitmapCutter.Core是圖片的伺服器端處理常式, 類圖為:
簡單說明下, 更多說明可查看源碼注釋 :
Cutter為裁剪對象, 用於儲存用戶端通過AJAX提交的資料.
Helper為圖片處理類, 包括圖片翻轉(RotateImage()), 圖片裁剪(GenerateBitmap()).
Callback為伺服器端圖片處理類, 通過使用Cutter封裝用戶端AJAX提交的資料, 然後調用Helper中的方法來完成圖片處理.
BitmapScissors是一個HttpHandler, 通過用戶端返回的 'action' 來調用Callback中的方法:
注: 此處用了反射來根據'action'值動態調用Callback中的方法, 如不習慣或認為不妥的請自行更改...
用戶端AJAX提交的Url為"scissors.axd", 實際為BitmapScissors類(如修改了伺服器端處理常式, 請在Web.config中根據實際進行httpHandler節點配置), 本例為:
Ok, 下面開始介紹重點, $.fn.bitmapcutter外掛程式的實現:
先來上張,標記下bitmapcutter中的Dom對象, 額, 喜歡剛大木的漫迷比較多, 就來張洛克昂的吧:
說明:
holder為原圖承載容器, 當原圖過大時可只顯示部分, 通過平移來查看未顯示地區的圖象, holder為div, 設定position樣式為relative, 這樣原圖img(同為img對象)的position樣式為absolute時, 即可通過控制其left, top 值來進行圖片的平移顯示, 注意holder的overflow必須設定成hidden, 這樣img將會被包裹在holder中,超出holder邊界的img將會被隱藏.
cutter為截取框, 可進行拖拽以選擇自己敢興趣的截取地區, 拖拽地區在holder容器內, 同時會在thumbimg(原理與holder+img相同)內產生縮圖.
opts - 功能區域, 提供所有可用的功能按鈕, 包括: 放大, 縮小, 左移, 右移, 上移, 下移, 還原, 左旋轉, 右旋轉.
info - 圖片相素, 用於顯示當前待截取圖片的寬高比.
原理:
熟悉css的話應該很容易看出來, 利用相對定位的靈活性和js對css的控制.
外掛程式說明:
由於外掛程式的代碼比較多, 這裡就揀重點的講了:
1. 全域變數:
由於在整個裁剪過程中會頻繁的使用一些資料和jQuery對象, 所以定義了一個全域變數用來儲存.
$originalSize: 原圖的相素, 在首次載入原圖時擷取, 不可改變, 主要是為放大, 縮小和還原提供一對基礎資料(長,寬).
$zoomValue: 當前縮放比, 在執行放大或縮小時同步.
$thumbimg: 裁剪地區縮圖的jQuery對象, 在該dom被建立時賦值.
$img: 原圖jQuery對象, 在該dom被建立時擷取.
$cutter: 裁剪地區, 同樣在dom被建立時擷取.
2. 外掛程式:
為了方便圖象的控制, 同樣編寫了幾個附加外掛程式, 如果同學們在使用中有重名的請設法避免
$.fn.f: 用於擷取jQuery對象的樣式值, 例如width, height, left, top等.
$.fn.loadBitmap: 用於預載入圖片以擷取正確的相素.
$.fn.scaleBitmap: 用於縮放圖片..縮放比率來自全域變數$zoomValue.
$.fn.dragndrop: 我的上一個外掛程式$.fn.Drags的定製版, 主要是為了實現某一dom對象在特定元素內的拖拽.
3. API:
$.fn.bitmapCutter的API包括:
src(String): 待裁剪圖片的路徑(相對於程式主目錄), 預設為空白, 必須,
renderTo(String(Selector)|jQuery Object): bitmapCutter外掛程式容器, jQuery對象或選取器, 預設為$(document.body),
holderSize(Object): holder對象的大小, 包括width和height兩個值, 預設為{ width: 300, height: 400 },
cutterSize(Object): cutter對象的大小(產生頭像大小), 包括width和height兩個值, 預設為{ width: 70, height: 70 },
zoomStep(Float): 每次縮放的比率更改幅度, 預設為0.2,
zoomIn(Float): 放大時與原圖的最大比, 預設為2.0,
zoomOut(Float): 縮小時於原圖的最大比, 預設為0.1,
rotateAngle(Int): 圖片翻轉角度, 可取值為 90, 180, -90, -180, 預設為 90,
moveStep(Int): 原圖平移時的平移象素, 預設為100,
onGenerated(Function): 成功產生頭像時觸發事件數目, bitmapCutter向此方法傳遞一個參數 'src', 表示新裁剪產生的頭像路徑. 此屬性的預設值為 function(src) { },
lang(Object): 功能區域各按鈕的Tooltip, lang的API為:
zoomout(String): 放大, 預設值 'Zoom out',
zoomin(String): 縮小, 預設值'Zoom in',
original(String): 原始大小, 預設值'Original size',
clockwise(String): 順時針旋轉, 預設值'Clockwise rotation({0} degrees)', 需包含 {0} 以便格式化時使用rotateAngle值填充,
counterclockwise(String): 逆時針旋轉, 預設值'Counterclockwise rotation({0} degrees)', 需包含 {0} 以便格式化時使用rotateAngle值填充,
generate(String): 產生頭像, 預設值'Generate!',
process(String): 產生頭像時的提示, 預設值為'Please wait, transaction is processing......',
left(String): 左平移, 預設值'Left',
right(String): 右平移, 預設值'Right',
up(String): 上移, 預設值'Up',
down(String): 下移, 預設值'Down'
API原型為:
4. 開發技巧:
為了便於代碼管理及功能最佳化, 將功能進行了歸納, 大致可分為:
縮放(zoom): 細分為放大和縮小,
翻轉(rotate): 細分為順時針和逆時針,
移動(move): 細分為上|右|下|左平移.
所以直接編寫三個 '基類' (具體實現看源碼):
izoom(zv): 提供圖片放大或縮小功能, zv為當前縮放比,
irotate(angle): 提供圖片旋轉功能, angle為當前旋轉角度,
imove(direction): 提供原圖平移功能, direction為移動方向, 包括'left','up','right','down'.
那麼現在需要的就是細分各個功能:
這裡我使用了一個對象scissors來封裝這些操作, 使用call來 '繼承', 當然你也可以使用 '冒充' 或者 '原型鏈' 這些手段來實現. 這樣的話在綁定這些方法的時候使用和json就可以很輕鬆的完成:
看不明白?那麼貼上Html樹呢?
5. 自問自答
Q: 為什麼要在伺服器端來實現旋轉效果?
A: 為了實現福士化和瀏覽器安全色, 所以我放棄了濾鏡和Canvas.
Q: Photoshop等圖片處理軟體在裁剪時都是實現反相效果的, 為什麼不實現?
A: 我想一個程式兼顧UI的時候也要考慮效率, 如果實現反相的話, cutter對象內同樣需要內建一個img來與thumbimg同步, 那麼當旋轉圖象時記憶體的累積是非常恐怖的.
Q: 相比Flash, Siverlight而言這麼一個東西有什麼好處?
A: 額, 其實我最初是打算用Flash做的, 不過考慮到福士化的問題(多少.Net開發人員會as?), jQuery+HttpHandler是一個不錯的選擇, 開放的API, 開放的源碼, 我想遠比讓大家去改一個.fla來的輕鬆愉快.
Q: 那麼壞處?
A: 大家都知道, 瀏覽器的緩衝是把雙刃劍, 在bitmapCutter裡他就很煩人, 為了實現圖片旋轉後重新整理, 只能為img的src加上一個戳(隨機數?時間?whatever..), 但是這樣會帶來一個必然的結果 - 瀏覽器記憶體累積(目前已將此威脅降至最低 ;-)), 額..!@#$%
6. 注
未對gif類型圖片作特殊處理, 所以僅會取回第一楨處理, 產生頭像格式為Png, 如果需要的話自己修改源碼.
jquery.bitmapcutter.js 尾部方法為類比 c# string.format, 必須的.
支援鍵盤操作, 方向鍵控制平移, +-控制放大縮小, 不喜可從源碼刪除.
7. demo中的簡單樣本
使用一張C.C的圖來截取頭像, 設定截取框長寬為120:120, 容器為id為container的div, 每次旋轉角度為90, 順時針旋轉功能按鈕的提示為 '順時針旋轉{0}度.'
效果:
8. 源碼包(測試圖片多):
google code