前言
小程式分享只能分享到好友或者微信群裡,不能分享到朋友圈。
這周接到一個這樣的坑爹需求,小程式需要實現一個圖片分享的功能。讓使用者可以把圖片傳送到朋友圈或者其他渠道。
剛開始拿到這個需求,覺得還好,沒有太大的感覺, 第一個感覺想把鍋甩給後端,呼叫一個介面讓後端傳回來一個URL,前端只負責顯示就好。^_^
當然這麼天真的想法基本上不可能實現的,如果你遇到這樣的後端,請好好的珍惜他。
言歸正傳,前端自己做這個功能的話,必須要藉助一個東西,那就是 Canvas。
初用
Canvas 並不是小程式獨有的,html5裡也有。不過本人實在是用的不多。唯二兩次 其一做h5小遊戲的時候 另一次跟本次的需求很接近,也是把一個 div 裡的內容放入 canvas 並生成圖片 讓使用者可以保持。
我就按照之前的思路來做是沒問題的,小程式自己封裝了一套API, 不過 canvas 相關的 API 跟正常 html 的基本一樣, 我就按照以前 web 的方式再過一遍。
開用
首先在我們的wxml寫下如下程式碼
<canvas class="canvas" canvas-id="canvas"></canvas>
複製程式碼
設定 Canvas 大小,可以直接在 wxml 裡的 canvas 標籤上設定,也可以用 wcss 。個人比較喜歡用wcss 。
重點來了 canvas-id
是小程式裡特有的,是canvas 元件的唯一識別符號,之後的很多地方都需要用到。
然後我們就開始寫 js 了
// 這裡的 canvas 就是之前在 wxml 裡的 canvas-id
let ctx = wx.createCanvasContext(`canvas`)
複製程式碼
第一個坑的地方來了,正常來 page 裡用是一點沒問題沒有的,但是在元件裡的話,畫不了,就是顯示不出來。
查文件能發現下列一段話
建立 canvas 繪圖上下文(指定 canvasId)。在自定義元件下,第二個引數傳入元件例項this,以操作元件內
<canvas/>
元件Tip: 需要指定 canvasId,該繪圖上下文只作用於對應的<canvas/>
很顯然,我們就程式碼改成如下即可
let ctx = wx.createCanvasContext(`canvas`, this)
複製程式碼
在相應的位置畫文字及圖片
具體怎麼畫,我就不講了,直接開始講坑的點。
在相應的位置
沒錯,就一個很坑的點。canvas 是居中的,而且在不同尺寸的螢幕上各個點的位置都不一樣。我們需要獲取螢幕的寬度以及高度進行計算。(主要是寬度)
小程式提供瞭如下方面,讓我們來獲取裝置的一些資訊
wx.getSystemInfo({
success: (res) => {
this.setData({
windowWidth: res.windowWidth,
windowHeight: res.windowHeight,
})
}
})
複製程式碼
文字
這個是最坑的一個點
看UI給的樣式,中間會有一段兩行的文字。
開始拿到的時候,覺得so easy。算了每一行的字數,擷取兩段就不好了嘛。 最後出來的效果確實很坑爹。有長有短,還有超過界限的。
這樣明顯不能滿足我們的需求。而之所以會造成這樣的問題。就是因為我們的內容是中英文混合的,中文佔兩個字元長度而英文只佔一個,我們根據字串的length
擷取時,中英文都是1。
除非是純英文或者純中文,才能對齊。怎麼解決呢? 上程式碼!
getContent(detail, length = 24, row = 2) {
let len = 0
let index = 0
let content = []
for (let i = 0; len < detail.length; i++) {
// 若未定義則致為 ``
if (!content[index]) content[index] = ``
content[index] += detail[i]
// 中文或者數字佔兩個長度
if (detail.charCodeAt(i) > 127 || (detail.charCodeAt(i) >= 48 && detail.charCodeAt(i) <= 57)) {
len += 2;
} else {
len++;
}
if (len >= length || (row - index == 1 && len >= length - 2)) {
len = 0
index++
}
if (index === row) break
}
return content
}
複製程式碼
我們可以使用 charCodeAt
來獲取 charCode,中文的話在 127以上(ps.測試的時候發現 數字也是佔兩個長度的)。
為了預防之後需求的變動 我把這塊提取出來寫了一個方法。之後只用傳入內容,每行的字數以及行數就好。
圖片
這裡有兩個坑
- 不能直接使用網路圖片,在真機上畫不出來
- 不能圓角,圖片如果是方形的 不會再處理
網路圖片使用
既然不能直接使用網路圖片,那麼我們換個思路,把圖片下載到本地,在顯示出來是不是就行了。
- 下載網路圖片
wx.getImageInfo({
src: url,
success: (res) => {
// 下載成功 即可獲取到本地路徑
console.log(res.path)
}
})
複製程式碼
直接用這個你會發現,會出錯。小程式對能下載的圖片做白名單處理。即需要在 小程式後臺 > 設定 > 伺服器域名 > downloadFile合法域名
裡設定網路圖片的域名。
ps.因為域名要求是https的, 並且一個月只能修改五次,建議把需要下載的網路圖片放在自己的https的伺服器上,再走個CDN什麼的。
- 繪製圖片
接下來跟繪製本地圖片一樣
ctx.drawImage(res.path, left, top, width, height);
複製程式碼
圖片圓角處理 (本例示範的是圓形,原理一樣)
整體思路是畫一個圓形區域,再在這塊區域把圖片畫上去,最後進行合併擷取就生成了圓形圖片。具體程式碼如下所示
drawImage(ctx, url, left, top, width, height) {
// 儲存當前環境的狀態
ctx.save();
// 起始一條路徑,或重置當前路徑
ctx.beginPath();
// 畫一個圓
ctx.arc(width / 2 + left, height / 2 + top, width / 2, 0, Math.PI * 2, false);
// 從原始畫布剪下任意形狀和尺寸的區域
ctx.clip();
wx.getImageInfo({
src: url,
success: (res) => {
// 向畫布上繪製影像
ctx.drawImage(res.path, left, top, width, height);
// 返回之前儲存過的路徑狀態和屬性
ctx.restore();
// 畫出來
ctx.draw();
}
})
},
複製程式碼
會發現下載圖片是一步非同步操作,如果只是一個圖片還好,如果是多個的話,就會非常的亂,我們可以用 Promise
優化下
drawImage(ctx, url, left, top, width, height) {
ctx.save();
ctx.beginPath();
ctx.arc(width / 2 + left, height / 2 + top, width / 2, 0, Math.PI * 2, false);
ctx.clip();
return new Promise((resolve, reject) => {
wx.getImageInfo({
src: url,
success: (res) => {
ctx.drawImage(res.path, left, top, width, height);
ctx.restore();
resolve()
},
fail: (e) => {
reject(e)
}
})
})
},
複製程式碼
把當前畫布指定區域的內容匯出生成指定大小的圖片,並返回檔案路徑。
使用微信提供的canvasToTempFilePath
savePic () {
let that = this;
let offset_left = (this.data.windowWidth - 303) / 2
console.log(`savePic`)
wx.canvasToTempFilePath({
x: offset_left,
y: 0,
width: 303,
height: 398,
canvasId: `canvas`,
success: function (res) {
console.log(res.tempFilePath)
},
fail (e) {
console.log(e)
}
}, this)
}
複製程式碼
預覽/儲存圖片
上一步我們已經把把canvas的內容儲存生成圖片了。繼續來其實有兩種方案。
- saveImageToPhotosAlbum, 把該檔案儲存到相簿裡去
- previewImage,直接預覽該圖片
其實兩種方法都行,可以根據實際的需求做相應的選擇。
我在選擇的時候採用了 previewImage
,因為這個不需要使用者授權, 使用者可以直接把這個圖片傳送給朋友 並不需要儲存圖片。如果想要儲存圖片的話,在預覽的時候 也可以儲存。
小感
以前覺得看文章覺得也就那麼回事,自己寫的時候才發現,是多麼的難產。也許還比較菜吧,繼續努力。這是我在掘金上釋出的第三篇文章
前兩篇分別如下,有需要的話 可以看看
最近在寫 DApp, 基於 nebulas 公鏈的。5月到7月兩個月時間有一個激勵計劃,上架一個DApp 即可獲得 110 NAS 約等於 6000 RMB,周大獎約為100w RMB,不過這個基本上不考慮了, 但是上架一個 DApp 也算一個不小的收入。有興趣的小夥伴可以去註冊下。註冊連結
剛開始一週, 本人提交了兩個DApp, 遇到了一些坑吧 在本週會寫一個新手教程出來,幫助大家更好的開發DApp