canvas菜鳥基於小程序實現圖案在線定制功能
前言
最近在搗騰一個 襪子圖案在線定制
的小程序,核心的需求大概是選擇一只純色的襪子,然後客戶可以在襪子上面添加圖案,
最終生成一個設計圖保存後服務器。定制的圖案可以旋轉,縮放和拖動,當然,還可以刪除。
內容比較初級, 因為我也只是一個 canvas 新手,本文僅是一次實踐的記錄.
實現-繪圖
其實剛開始接手這個項目的時候,我是打算用小程序的 web-view 組件來嵌套 h5 的,
因為小程序的 canvas 的坑,或者說小程序的坑,我是深有體會的,不過回過頭來想,
web-view 我還沒再項目中用過, 還說不定會遇到什麽更大的坑呢,所以最終還是選擇小程序的 canvas
首先, 計算圖片的大小和位置,不能讓圖片超過畫布(我這裏是不能超過畫布的 80%),位置在畫布的正中間.
然後, 需要將原點移動到畫布的中心點 這點比較重要,等下下面的旋轉如果沒有這一步的話,會沿著圖片的左上角旋轉.
第三步,將目標圖像畫到畫板上,找個是沒啥難的,直接調用 ctx.drawImage
既可
if (!this.imgUrl) return
ctx.drawImage(this.imgUrl, x, y, this.tempImgWidth, this.tempImgHeight)
drawImage 之前需要 判斷一下目標圖像的路徑是否可以拿到圖片,如果圖片路徑有問題,會直接報錯,並且影響接下來的代碼執行,
相當於整個應用崩潰了.
我這裏因為都是本地圖片,所以只要判斷圖片存不存在即可,如果是網絡圖片,還要先保存成本地圖片再 drawImage,
不然有可能會出現很多畫不出來的可能性.
由於中心點已經偏移了,這個時候, drawImage 的 x 和 y 需要向左上角偏移回去(imgWidth / -2, imgHeight / -2),
保持圖片的中心點跟canvas的原點重疊.
到此, 圖片就畫好了,如果有旋轉,要在這個時候旋轉.旋轉完以後,再畫外框和操作按鈕,
這樣可以保持外框和操作按鈕不隨著圖片旋轉(經過驗證,這種方式比跟著旋轉的好很多),
旋轉也很簡單,直接調用 ctx.rotate
即可, 旋轉的角度計算方法百度上有.我是直接拷貝的
然後就是操作按鈕和虛線框,參數跟 drawImage 一樣,因為是畫一個跟圖片一樣大小的框框嘛..
虛線框
ctx.setStrokeStyle(‘#fd749c‘)
ctx.setLineDash([5, 5], 10);
ctx.strokeRect(x, y, this.tempImgWidth, this.tempImgHeight)
操作按鈕,這裏的 r 是按鈕的半徑, d 是按鈕的直徑,無非就是在圖片的四個角,畫按鈕.
// 畫 刪除 按鈕
ctx.drawImage(‘/static/design/icons/delete.png‘, x - r, y - r, d, d)
// 畫 旋轉 按鈕
ctx.drawImage(‘/static/design/icons/rotate.png‘, x + this.tempImgWidth - r, y - r, d, d)
// 畫 縮放 按鈕
ctx.drawImage(‘/static/design/icons/scale.png‘, x + this.tempImgWidth - r, y + this.tempImgHeight - r, d, d)
最後再調用一下 ctx.draw()
完成繪圖. 所用到的 API 都很簡單,不過過程要計算的東西還是很多
實現-操作
繪圖已經完成了,那麽如何做到點擊或拖動操作按鈕,做響應的操作呢?
這就需要監聽 canvas 的 bindtouchstart, bindtouchmove, bindtouchend 三個事件,
然後 e.touches[0].x
和 e.touches[0].y
可以獲取當前手指的位置,
在 bindtouchstart 的回調裏面判斷當前位置,如果跟某個操作按鈕重疊,就說明是操做,如果都沒有重疊,則是拖動,然後把操作記錄下來,
在 bindtouchmove 的回調裏面,獲取手指移動的距離和角度,然後根據 bindtouchstart 裏記錄的操作,
調整對應的參數(拖動跟縮放,只要計算當前點的 x,y 跟 bindtouchstart 點的 x,y 的偏移量即可,旋轉的上面有提過,百度有),
然後重新調用 ctx.draw()
重新繪圖.
一個操作做完,可能需要復位一些東西,這個可以在 bindtouchend 的回調裏面去做
tips: 這裏的
e.touches[0].x
的 e 是回調函數帶的參數
保存定制圖
這些都畫好以後,需要把定制圖保存起來,指定圖保存的不單單是原來那個 canvas裏的圖,還要連底圖(也就是那張襪子圖)一起保存起來,
需要一個襪子圖那麽大的 canvas(這個稱為臨時 canvas 吧),先把襪子畫進去,然後再把圖案畫上去,畫圖案上去的時候,由於畫布大小位置已經變了,
所以需要稍微計算一下,大概就是 x,y 要分別加上原來的 canvan 跟襪子圖左上角的距離.然後還要把外框跟操作按鈕去掉.
最後調用 ctx.draw
把圖畫出來..這裏有點需要註意的是,draw 其實是個異步函數,如果你在ctx.draw()
下一行做保存操作,
那麽你保存的會是一個空白的圖片,draw 函數的第二個參數,是一個回調函數,想要獲取畫完圖的 canvas,要在這個回調函數裏面才能拿的到
ctx.draw(false, function () {
// 要先保存到臨時路徑
wx.canvasToTempFilePath({
canvasId: ‘tempCanvas‘,
success: function (res) {
// 然後再保存到相冊
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success () {
wx.showToast({
title: ‘保存成功!‘,
icon: ‘none‘,
duration: 2000
})
}
})
}
})
}
)
你感受到了微信小程序團隊滿滿的惡意了嗎? 這大概是我第一次寫這麽深的嵌套吧..傳說中的回調地獄.雖然可以用 promise 封裝解決,
不過為了幾個幾乎不會復用的 API 去封裝似乎不太劃算. 不過總算還是保存成功了.
tips: 為了保存的時候這個臨時 canvas 不影響正常界面,這我的做法是對 臨時 canvas 設置 position:fixed; left:100vw;這兩個樣式,把它隱藏起來
最後
本文用的代碼都是基於 mpvue 框架的, 這個也是個比較基礎的東西,就不整個項目開源浪費 github 的空間了,
我把跟 canvas 有關代碼上傳到這裏,
僅供參考,單獨運行是肯定運行不起來, 如果有需要源碼,可以私下發郵件找我要.
本文的的地址 個人技術帖合集 歡迎隨意 star
和 follow
, 和不隨意的 issue
canvas菜鳥基於小程序實現圖案在線定制功能