前言
當初接觸到 rxjs 這套以流的方式處理事件及資料的庫時,覺得非常新奇,便嘗試自己從頭寫了一個 rxjs + vuerx + vue 支援觸控端、響應式的圖片剪裁元件,取名叫 vuejs-clipper 併發布至 npm。
算是一個簡單的元件,這篇主要介紹下當初的一些有趣的思考方向,而非程式方面如何實作。
完整的程式碼、使用範例都在連結裡,有興趣、喜歡的話可以使用看看或給個星當做鼓勵 :)
vuejs-clipper 展示
結構
前端使用Vue,要在Vue Component中使用rxjs的話,vuerx這個Vue的外掛(Plugin)提供兩者很好的結合,讓Vue component多了個subscriptions
選項可定義 rxjs 的 subject 和 Observable 等。
以及提供v-stream
,和v-on
一樣監聽Dom事件,但是以rxjs Observable流式來處理。
元件使用Vue SFC方式開發,最後用@vue/cli 打包成umd擋案可用script引入。
構想
(以下圖多注意)
其實已經有許多圖片剪裁元件的庫了,像是這個cropper.js,功能應該非常強大齊全(沒有使用過...汗顏),還有結合cropper.js和Vue component的vue-cropperjs等等......
這邊打個岔,先來定義一些名詞以方便我後面繼續講下去。。。
這張圖以vuejs-clipper為例
如果剛剛有玩一下cropper.js的話可以發現,他的剪裁匡和圖片都是可以縮放和移動的。
不過當初我的想法是,其中一個往左移動,不就相對於另一個元素往右移動了;其中一個元素縮小,就相對於另一個元素放大了啊。所以我認為圖片或剪裁匡,選擇其中一個進行縮放和移動就好。
基於這個原則我做了兩種不同的元件,取名叫 clipper-basic(示例) 和 clipper-fixed(示例)。
clipper-basic
手指/滑鼠在剪裁元件上只能縮放移動剪裁匡,但可以另外繫結屬性來控制圖片旋轉和放大。
clipper-fixed
手指/滑鼠在剪裁元件上只能縮放移動圖片,但可以另外繫結屬性來控制圖片旋轉。
元件設計
接下來介紹一些設計、優化的重點。
位置、大小使用百分比
剪裁匡、圖片都是剪裁元件的子元素,長寬(width, height)、位置(top/bottom, left/right),需要用相對外匡的百分比來表示,否則熒幕大小一變就會跑版。
如果能使用 transform: translate 來替代layout: top/bottom, left/right 的話效能會更好,但當初開始寫的時後我剪裁匡的位置和長寬並非一起計算,render的順序不一致導致我用 translate 的話常常會跑版,後來就乾脆用 top/bottom, left/right 來表示位置了。。。
拖弋剪裁匡
如何實作拖弋剪裁匡相信大家都會的:
紅點是一開始點選的位置(基準點,跟著藍色匡移動)
按著滑鼠/手指從P1移動到P2
移動匡(藍色匡)移動後位置(以top, left表示的話):top 即是 y2 - y1
,left 則是 x2 - x1
當然我們還要判斷藍色匡不能超出黑色外匡, 計算出來的 top/left 需要介於 min top/left 和 max top/left 之間。
上圖演示一個例子,當滑鼠移動到 藍點1 時藍色匡並不會移動因為已經抵達邊界,但如果使用者依然按著滑鼠/手指並移回 藍點2,會發現藍色匡還是沒有移動,這是因為基準點 紅點 並沒有改變,造成在灰色斜線的區域移動滑鼠,藍色匡都不會移動,是一個不太好的體驗。
解決方法:當藍色匡抵達邊界,但滑鼠/手指仍在移動時,基準點跟著改變且不超出邊界,這樣就不會有滑鼠白白移動的感覺。
以下示範滑鼠從P1點選移動到P2,P3,P4,紅色基準點跟著改變,這樣當滑鼠一往回移動藍色匡就可以跟著移動。
拖弋圖片的話就比較沒有這個問題,因為圖片是可以超出邊界的。
縮放剪裁匡
縮放剪裁匡其實就是判斷一開始滑鼠點在匡上的哪個方位,就使用對秤點當作基準點,剪裁匡以該點不動來縮放。
可以發現Cropper.js是有八個基準點可以縮放的:
但當初的 clipper-basic 設計成剪裁匡是可以縮放成任意比例的,因此邊上的基準點就不需要了,只做了四個基準點:
其實在任意比例 (長寬比例不限 ) 下這樣設計是沒問題的,但後來想要加上限制比例的設定就比較尷尬了,可以去展示把 ratio 選項打勾並縮放剪裁匡試試,因為少了邊上的基準點,在水平或垂直縮放時用起來有些奇怪,但是對角縮放就不會。
剪裁元件長寬
正常來說圖片剪裁元件可以設定外觀長寬,但一開始 clipper-basic 並沒有要做圖片縮放,並想把這個元件做成像一個 <img>
元素,根據圖片自動調整比例,但是隻做了根據寬度調整高度,而沒有設定高度來調整寬度的選項。。。
這就造成一個問題,假設圖片剪裁元件設定 width: 500px
,當上傳一個很長的圖片時,網頁的佈置會被拉的很長。
算是設計上的疏失,可能的解決辦法寫在這裡。
clipper-fixed 的話是有設定剪裁比例的,長寬比則是看剪裁比例相同。
縮放圖片
clipper-fixed 縮放圖片,繫結滾輪/兩指觸控事件,會發現如果使用固定比例縮放(假設每次縮放0.1倍好了),在圖片較小時會覺得縮放的很快,圖片很大時縮放則感覺非常緩慢。
因此需要用線性方式來調整縮放比例: (下圖只是示例,非真正的比例)
縮放率要隨著圖片的大小增加,公式的話…一元、二元應該都是可以的,調整到自己感覺最舒適的引數。
輸入和輸出
想要表現的像一個<img>
,因此有src
屬性,接受一個圖片的 URL。 輸出則是一個canvas element,使用者可將canvas繪製到其他地方,或者轉成image元素、Blob、URL等,這些操作是比較消耗效能和時間的。
設計這個元件(而非函式庫)就是想要間單一點,單純控制UI,不介入一些上傳、轉換圖片的處理。
結語
總結一下這次作這個元件可以改善的地方,以後(應該是不會有以後.w.)可以改進:
- 剪裁元件的長寬設定方式不佳
- 剪裁匡縮放基準點可以設為八個
- 剪裁元件的初始狀態無法設定 (一開始剪裁匡的位置、大小等)。
個人認為 clipper-fixed 這種操作方式是最通用也最沒毛病的,但我也不懂一些UI/UX啥的,不知道怎樣使用者操作比較順手。。。
雖然說是用 vue + vuerx + rxjs 但本文幾乎沒有講到這三者的時作和程式碼,因本人也算是剛接觸的生手,尚在摸索,就沒有對這幾個庫作講解。
就先介紹到這,希望大家會喜歡。