Vue 圖片剪裁元件設計思考

timtnlee發表於2018-11-11

前言

當初接觸到 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為例

Vue 圖片剪裁元件設計思考

如果剛剛有玩一下cropper.js的話可以發現,他的剪裁匡和圖片都是可以縮放移動的。

不過當初我的想法是,其中一個往左移動,不就相對於另一個元素往右移動了;其中一個元素縮小,就相對於另一個元素放大了啊。所以我認為圖片或剪裁匡,選擇其中一個進行縮放和移動就好。

基於這個原則我做了兩種不同的元件,取名叫 clipper-basic(示例) 和 clipper-fixed(示例)。

clipper-basic

手指/滑鼠在剪裁元件上只能縮放移動剪裁匡,但可以另外繫結屬性來控制圖片旋轉和放大。

Vue 圖片剪裁元件設計思考

clipper-fixed

手指/滑鼠在剪裁元件上只能縮放移動圖片,但可以另外繫結屬性來控制圖片旋轉。

Vue 圖片剪裁元件設計思考

元件設計

接下來介紹一些設計、優化的重點。

位置、大小使用百分比

剪裁匡、圖片都是剪裁元件的子元素,長寬(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

Vue 圖片剪裁元件設計思考

當然我們還要判斷藍色匡不能超出黑色外匡, 計算出來的 top/left 需要介於 min top/left 和 max top/left 之間。

Vue 圖片剪裁元件設計思考

上圖演示一個例子,當滑鼠移動到 藍點1 時藍色匡並不會移動因為已經抵達邊界,但如果使用者依然按著滑鼠/手指並移回 藍點2,會發現藍色匡還是沒有移動,這是因為基準點 紅點 並沒有改變,造成在灰色斜線的區域移動滑鼠,藍色匡都不會移動,是一個不太好的體驗。

解決方法:當藍色匡抵達邊界,但滑鼠/手指仍在移動時,基準點跟著改變且不超出邊界,這樣就不會有滑鼠白白移動的感覺。

以下示範滑鼠從P1點選移動到P2,P3,P4,紅色基準點跟著改變,這樣當滑鼠一往回移動藍色匡就可以跟著移動。

Vue 圖片剪裁元件設計思考

拖弋圖片的話就比較沒有這個問題,因為圖片是可以超出邊界的。

縮放剪裁匡

縮放剪裁匡其實就是判斷一開始滑鼠點在匡上的哪個方位,就使用對秤點當作基準點,剪裁匡以該點不動來縮放。

可以發現Cropper.js是有八個基準點可以縮放的:

Vue 圖片剪裁元件設計思考

但當初的 clipper-basic 設計成剪裁匡是可以縮放成任意比例的,因此邊上的基準點就不需要了,只做了四個基準點:

Vue 圖片剪裁元件設計思考

其實在任意比例 (長寬比例不限 ) 下這樣設計是沒問題的,但後來想要加上限制比例的設定就比較尷尬了,可以去展示把 ratio 選項打勾並縮放剪裁匡試試,因為少了邊上的基準點,在水平或垂直縮放時用起來有些奇怪,但是對角縮放就不會。

剪裁元件長寬

正常來說圖片剪裁元件可以設定外觀長寬,但一開始 clipper-basic 並沒有要做圖片縮放,並想把這個元件做成像一個 <img> 元素,根據圖片自動調整比例,但是隻做了根據寬度調整高度,而沒有設定高度來調整寬度的選項。。。

這就造成一個問題,假設圖片剪裁元件設定 width: 500px,當上傳一個很長的圖片時,網頁的佈置會被拉的很長。

Vue 圖片剪裁元件設計思考

算是設計上的疏失,可能的解決辦法寫在這裡

clipper-fixed 的話是有設定剪裁比例的,長寬比則是看剪裁比例相同。

縮放圖片

clipper-fixed 縮放圖片,繫結滾輪/兩指觸控事件,會發現如果使用固定比例縮放(假設每次縮放0.1倍好了),在圖片較小時會覺得縮放的很快,圖片很大時縮放則感覺非常緩慢。

因此需要用線性方式來調整縮放比例: (下圖只是示例,非真正的比例)

Vue 圖片剪裁元件設計思考

縮放率要隨著圖片的大小增加,公式的話…一元、二元應該都是可以的,調整到自己感覺最舒適的引數。

輸入和輸出

想要表現的像一個<img>,因此有src屬性,接受一個圖片的 URL。 輸出則是一個canvas element,使用者可將canvas繪製到其他地方,或者轉成image元素、Blob、URL等,這些操作是比較消耗效能和時間的。

設計這個元件(而非函式庫)就是想要間單一點,單純控制UI,不介入一些上傳、轉換圖片的處理。

結語

總結一下這次作這個元件可以改善的地方,以後(應該是不會有以後.w.)可以改進:

  • 剪裁元件的長寬設定方式不佳
  • 剪裁匡縮放基準點可以設為八個
  • 剪裁元件的初始狀態無法設定 (一開始剪裁匡的位置、大小等)。

個人認為 clipper-fixed 這種操作方式是最通用也最沒毛病的,但我也不懂一些UI/UX啥的,不知道怎樣使用者操作比較順手。。。

雖然說是用 vue + vuerx + rxjs 但本文幾乎沒有講到這三者的時作和程式碼,因本人也算是剛接觸的生手,尚在摸索,就沒有對這幾個庫作講解。

就先介紹到這,希望大家會喜歡。

相關文章