1. 程式人生 > >canvas菜鳥基於小程序實現圖案在線定制功能

canvas菜鳥基於小程序實現圖案在線定制功能

als 圖. 不能 http 報錯 his 去掉 還要 ctu

前言

最近在搗騰一個 襪子圖案在線定制 的小程序,核心的需求大概是選擇一只純色的襪子,然後客戶可以在襪子上面添加圖案,
最終生成一個設計圖保存後服務器。定制的圖案可以旋轉,縮放和拖動,當然,還可以刪除。

內容比較初級, 因為我也只是一個 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].xe.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 有關代碼上傳到這裏,
僅供參考,單獨運行是肯定運行不起來, 如果有需要源碼,可以私下發郵件找我要.

本文的的地址 個人技術帖合集 歡迎隨意 starfollow, 和不隨意的 issue

canvas菜鳥基於小程序實現圖案在線定制功能