SimpleCrop 目前是全網唯一支持裁剪圖的任意角度旋轉、交互體驗媲美原生客戶端的全平臺圖片裁剪組件。
項目地址:https://github.com/newbieYoung/Simple-Crop
特性及優勢
和目前流行的圖片裁剪組件相比,其優勢在於以下幾點:
- 裁剪圖片支持任意角度旋轉;
- 支持 Script 標籤、微信小程序、React、Vue 等多種開發模式;
- 支持移動和 PC 設備;
- 支持邊界判斷、當裁剪框裡出現空白時,圖片自動吸附至完全填滿裁剪框;
- 移動端縮放以雙指中心為基準點;
- 交互體驗媲美原生客戶端。
示例
移動端示例
左側是 IOS 系統相冊中原生的圖片裁剪功能,右側為 SimpleCrop 移動端示例。
可以掃描二維碼體驗:
或者訪問以下鏈接:
newbieyoung.github.io/Simple-Crop…
PC 示例
鏈接如下:
newbieyoung.github.io/Simple-Crop…
關鍵實現
要實現任意角度旋轉、雙指中心縮放、邊界判斷、自動吸附等功能,關鍵點如下:
1、屏幕座標系和變換基準點
在裁剪圖片場景中,存在兩個座標系,其一是裁剪圖片所代表的實際尺寸座標系,其二是裁剪框顯示到屏幕上所代表的屏幕座標系;後續進行 transform 變換計算和位置判斷時,為了計算方便,需要把裁剪圖片的尺寸以及位置從實際座標系轉換為屏幕座標系。另外當對裁剪圖片進行 transform 變換時,變換基準點默認為其中心點,對應 CSS 的 transform-origin 為 50% 50%。
2、獲取實時座標
首先需要實時獲取裁剪圖片進行 CSS Transform 變換後的新座標,只有在實時獲取變換後的新座標的前提下才能結合裁剪框座標進行越界、吸附等判斷;
在計算 CSS Transform 變換後的新座標時需要注意選取的屏幕座標系和 CSS Transform 座標系的差別,比如示例中以黑色邊框中心為座標原點,水平向左為 X 軸正方向,垂直向上為 Y 軸正方向;但是 CSS Transform 的座標系垂直向下為 Y 軸正方向和上述規定的座標系 Y 軸正方向是相反的,因此在獲取 CSS Transform 變換矩陣之後求實時座標時還需要進行鏡像變換。
詳細計算過程可以查看 CSS3 2D Transform Matrix。
3、旋轉適配縮放
裁剪圖片任意角度旋轉時需要進行適當的放大才能保證裁剪框不超出,因此就需要先計算裁剪框哪些點超出,然後根據超出的點計算剛好包含的放大倍數。
當兩個矩形位置關係任意變換時計算相互之間有哪些點超出有兩種方案:
其一:
圖中左側紅色矩形代表裁剪圖片,黑色矩形代表裁剪框,如圖所示裁剪框頂點 A 超出了裁剪圖片。
連接矩形四個頂點和判斷點,然後計算四條連線之間的夾角,如果夾角之和小於 360 度,那麼該判斷點在矩形外;反之如果夾角之和等於 360 度,那麼該判斷點在矩形內。
<code>a1 + a2 + a3 + a4 < 360
b1 + b2 + b3 + b4 = 360
複製代碼/<code>
其二:
圖中黑色矩形表示裁剪圖片,點 A 表示裁剪框中超出裁剪圖片的某個頂點。
連接矩形中心點和判斷點,然後計算中心點和判斷點向量在矩形邊框向量上的投影長度(L1、L2),只要兩個投影長度中有任意投影長度大於其投影邊框長度(H1、H2)的一半即說明該點在矩形外。
另外還可以根據投影長度和其投影邊框長度的比例計算出矩形恰好包含該點的放大係數,也就是示例圖中的 S 變量。
最後旋轉圖片時除了要進行適當的放大,保證裁剪框不超出以外,還可以在裁剪圖片中心點沒有變動時進行適當的縮小,去掉多餘間隙,進一步提升交互體驗。
縮小系數的計算原理和放大係數的計算原理類似,均是連接判斷點和中心點,然後根據邊框投影長度計算。
大矩形為裁剪圖片,小矩形表示裁剪框,O 表示裁剪圖片中心點。
4、雙指中心位移
由於默認裁剪圖片的變換基準點為其中心點,這麼處理雖然計算方便,但是會對雙指縮放造成一定的困難;因為雙指操作時裝指中心並不一定是裁剪圖片中心。
解決方案需要先求出兩個不同基準點的位移差,然後在進行縮放變換之後再進行位移變換。
5、縮放適配變換
在旋轉裁剪圖片時可以對其進行適當得放大和縮小從而保證裁剪框不會超出裁剪圖片;但是在雙指操作縮放裁剪圖片卻不能這麼做,因為適配縮放會和用戶的操作縮放衝突,因此需要採用移動裁剪圖片的方式保證裁剪框不超出裁剪圖片。
當裁剪圖片進行位移變換之後可以包含裁剪框,就只需要計算位移向量;
紅色矩形為裁剪圖片,黑色矩形為裁剪框。
但是還有一種情況即裁剪圖片進行位移變換之後不能包含裁剪框,如下:
紅色實線矩形為裁剪圖片,黑色矩形為裁剪框,紅色虛線矩形為進行放大之後恰好包含裁剪框的裁剪圖片。
此時說明用戶的操作縮放超出了組件的合法限制範圍,可以加入適配縮放了;這時候就需要先計算裁剪圖片恰好包含裁剪框的放大係數,然後再進行位移變換。
6、其它
在整個實現過程中還涉及到大量 Canvas API 操作,需要注意以下幾點;
- 使用微信小程序 type 2d Canvas API 時遇到了一些坑。比如:在 Android 中 Canvas 不支持 transform style,而在 IOS 中 Canvas 設置 width 和 height 屬性之前需要先設置其 style 的 width 和 height 否則不會生效;
- Canvas 元素的尺寸在瀏覽器中是有最大限制的,超出後瀏覽器會報警告canvas area exceeds the maximum limit 而且繪製的結果為空白;
- 使用 Image 標籤展示圖片時瀏覽器會忽視圖片的方向角數據,但是使用 Canvas API 繪製時又會考慮方向角;在處理圖片時需要注意不同標籤間的差異。建議的方案是使用 Exif.js 獲取圖片元數據,然後借鑑 JavaScript-Load-Image 中的處理方法即可。
閱讀更多 Echa攻城獅 的文章