接下來,我會把純前端實現生成帶二維碼的海報全流程給大家講個明明白白,把我自己遇到的坑,給大家詳細分享並講解,防止大家遇到相似問題,即使遇到問題,也會有一個明確的方向,並且吐血建議大家收藏一波,以備不時之需。(你不能保證以後的需求,沒有類似的吧,有的話,記得翻出來看看)
長路漫漫,總得有人敢於邁出踩坑的第一步,我想我就是那個😂😂,希望大家能踏著我冒著抬上救護車危險填平的坑,通向小程序生成海報勝利之路。送人玫瑰,手留餘香,閱讀的朋友可以給筆者 點讚,關注一波 陸續更新超幹,超硬核的前端文章。
一 寫在前面1 canvas繪製帶二維碼的海報,這些坑總有一個你可能會踩到,我會帶你一步步解決這些坑技術選型背景:taro3.0-vue
先來十一個問題壓壓驚,相信你做繪製海報過程中,一定會遇到
taro框架遇到的坑① taro-vue createCanvasContext 獲取canvas實例無效問題,繪製不出來效果?✅
② taro-vue 初始化獲取不到canvas上下文怎麼辦,完全繪製不出來圖片?✅
小程序canvas遇到的坑③ 關於canvas 寬高以及縮放比問題,繪製的元素變形,畫布的高度真得等於cavans標籤設置的寬高麼?✅
④ canvas怎麼繪製疊在一起的兩張圖片,並控制層級?✅
⑤ 如何用canvas繪製,多行文本?✅
⑥ 如何根據設計稿,精確還原海報各個元素位置問題。✅
⑦ canvas怎麼繪製base64的圖片✅
⑧ 如何繪製網絡的圖片,兩種canvas畫布api,繪製圖片有什麼區別完成✅
生成二維碼遇到的坑⑨ 如何正確選型生成二維碼工具?✅
⑩ 生成的二維碼,識別不出來怎麼辦,✅
⑪ 如何繪製二維碼上的logo✅
2 實現效果二 實戰一第一階段:小程序canva初始化1 兩種cavnas獲取上下文方式我們即將解決的問題
① taro-vue createCanvasContext 獲取canvas實例無效問題,繪製不出來效果?
② taro-vue 初始化獲取不到canvas上下文怎麼辦?
微信官網上介紹兩種 canvas 獲取上下文方式,一種是老的api ,一種是新的 api ,接下來我將講解一下這兩api的用法。
老版本 createCanvasContext 方式createCanvasContext是微信提供的獲取 canvas實例的老得接口,使用方式如下。
wxml
<canvas style="width: 300px; height: 200px;" canvas-id="firstCanvas"></canvas>
美好的一天從寫一個hello,world開始。
js中這麼寫
onReady(){
/* 使用 wx.createContext 獲取繪圖上下文 context , firstCanvas 與 canvas 屬性中的canvas-id一一對應 */
const context = wx.createCanvasContext('firstCanvas')
/* 設置字體大小 */
context.setFontSize(20)
/* 設置字體顏色 */
context.setFillStyle('pink')
/* 設置文本內容,位置 */
context.fillText('hello,world', 0, 0)
context.draw()
}
老版本是使用createCanvasContext傳入 canvas標籤中的 canvas-id屬性,來獲取canvas實例,老版本的使用起來說實話,不夠靈活,很多對canvas線條,顏色的設置,都封裝成方法了,每次改變需要調用方法。
新版本 getContext 上下文方式新的方式,則是先通過 createSelectorQuery 獲取 canvas 元素節點, 然後通過 getContext 獲取上下文。
wxml
<canvas type="2d" id="myCanvas"></canvas>
js
const query = wx.createSelectorQuery()
query.select('#myCanvas')
.fields({
node: true,
size: true
})
.exec((res)=>{
const { node } = res[0]
if (!node) return
/* 獲取 canvas 實例 */
const context = node.getContext('2d')
context.fillStyle = 'pink'
/* 設置字體樣式 大小 字體類別 */
context.font = 'normal 400 12px PingFangSC-Regular',
context.fillText('hello,world', 0, 0)
})
這種方式和第一種 createSelectorQuery 方式,在api使用方式上會有微妙的差別,這種寫法更像原生的DOM寫法,設置顏色,樣式,直接改變context屬性,而不再需要調用對應的api。
taro-vue 使用 canvas解決問題:① taro-vue createCanvasContext 獲取canvas實例無效問題,繪製不出來效果?
因為我們小程序技術選擇是 taro-vue2,所以我這裡重點講一下,在taro-vue中,目前使用 createCanvasContext 方式獲取 canvas 實例,繪製畫布從來沒有成功過,即便是createCanvasContext能夠創建上下文,但是任何東西也畫不出來(傳this之類的方案試了一個遍)。要是問我為什麼?實際我也不知道,只有凹凸實驗室的同學應該更清楚,GitHub上也有issue,希望taro團隊能夠重視起來。
解決方案就是採用最新的api,就是上述講的第二個方案。代碼如下:
import Taro from '@tarojs/taro'
const query = Taro.createSelectorQuery()
query.select('#myCanvas')
.fields({
node: true,
size: true
})
.exec(res=>{
//TODO:....
})
② taro-vue 初始化獲取不到canvas上下文怎麼辦?
在使用taro-vue的過程中,會面臨一個問題,就是小程序node節點獲取不到的問題,這個有可能是小程序本身的生命周期,和vue生命周期混亂造成的。尤其當我們選擇的是組件而不是頁面的情況。對於這樣的情況,官方文檔給出了答案。頁面首次渲染完畢時執行,此生命周期在小程序端對應小程序頁面的 onReady 生命周期。從此生命周期開始可以使用 createCanvasContext 或 createselectorquery 等 API 訪問真實 DOM。
也就是說如果想要獲取真是dom節點,我們可以這麼做,
組件中
mounted () {
eventCenter.once(getCurrentInstance().router.onReady, () => {
const query = Taro.createSelectorQuery()
query.select('#myCanvas')
.fields({
node: true,
size: true
})
.exec(res=>{
//TODO:....
})
})
}
尷尬的是,這種情況下,有的時候會造成 eventCenter.once() 回調函數不執行的情況,比如說當前組件的是受到v-if控制的情況。那麼怎麼樣解決呢,對於這種情況,我教大家一種解決方案。
我們可以用taro中,通過 Taro.nextTick 方法,將獲取元素的任務放在下一次nextTick執行。
mounted(){
Taro.nextTick(() => {
// 獲取元素
})
}
我們即將解決的問題:
③ 關於canvas 寬高以及縮放比問題,繪製的元素變形,畫布的高度真得等於cavans標籤設置的寬高麼?
<template>
<view>
<canvas
id="myPoster"
type="2d"
class="canves"
:style="canvasStyle"
/>
</view>
</<template>
在這裡我們首先要明白二個概念, 容器寬高: 我們給canvas標籤設置的寬高,就是如上代碼中的 canvasStyle,是canvas容器的寬高。 畫布寬高: 而我們畫布的寬高,在新版本api中,是通過獲取node節點,動態設置的node.width 和 node.height的值。
我們期望將整個屏幕作為畫布,對於不同手機,屏幕尺寸都會有差別,所以要動態獲取設備的寬高。這裡有一個問題是 容器寬高等於畫布寬高嗎 , 答案是否定的,為什麼這麼說呢,原因如下 小程序的canvas畫布有一個原始的畫布寬高,以及縮放比,而且是按照一倍像素來的,當我們給canvas容器設定容器寬高之後,如果沒有對應設置canvas畫布的畫布寬高以及scale,畫出的畫布就會嚴重的變形,我們用一個例子來解釋。
比如我們想再畫布上半部分區域,畫一個圖片,當我們期望正常比例畫 canvas ,如果我們只給cavans標籤加寬高,而不給畫布設置寬高的時候。會按照原始畫布的寬高比去繪製。
期望結果,畫布充滿屏幕,圖片按照正常比列展示。當我們不給 cavnas 畫布設置畫布寬高 以及縮放比的時候。會發生下面的情況。
實際效果:
所以我們初始化的時候要給canvas如下操作。這個在微信的官方文檔中,都有說明。
import Taro, {
eventCenter,
getCurrentInstance
} from '@tarojs/taro'
export default {
name:'myPoster',
data(){
const {
windowHeight,
windowWidth,
pixelRatio
} = Taro.getSystemInfoSync() /* 動態獲取設備的寬和高 */
return {
canvasStyle: { /* cavnas 的寬高 */
width: windowWidth + 'px',
height: windowHeight + 'px',
},
windowWidth,
pixelRatio, /* 屏幕縮放比 */
windowHeight,
scale:1
}
},
mounted(){
Taro.nextTick(() => {
const query = Taro.createSelectorQuery()
query.select('#myPoster').fields({
node: true,
size: true
}).exec(res => {
let {
node,
} = res[0]
if (!node) return
/* 第一步: canvas 畫布的寬高 和 元素的寬高 必須保持相同的長寬比列,否則會變形 */
const dpr = this.pixelRatio
const context = node.getContext('2d')
node.width = windowWidth * dpr
node.height = windowHeight * dpr
context.scale(dpr, dpr)
context.fillStyle = '#fff'
context.fillRect(0, 0, windowWidth, windowHeight)
})
})
}
}
當我們設置好畫布寬高,以及縮放比之後,就能按照正常比列進行繪製了。讓我們一起看看設置完縮放比之後的圖片效果,變成了我們想要的效果。
接下來就是繪製階段。
三 實戰第二階段:虛擬點位繪製canvas階段在講解canvas如何生成海報,完美還原設計稿的問題之前,我們應該想一個問題,因為canvas畫布,畢竟不是 dom模型,可以使用div或者view,通過自定義設置樣式來進行布局。cavnas需要我們畫出元素的布局效果,這裡就要精確獲取畫布上每一個元素相對與畫布的x,y值。那麼首先想到的是如何獲取每一個元素精確的x , y 值。
1 虛擬點位還原實際設計稿解決問題: ⑥ 如何根據設計稿,精確還原海報各個元素位置問題。 針對完美還原設計稿的問題,比較靠譜的方案就是,先1:1正常掛在dom元素,然後通過獲取元素的位置,來繪製canvas畫布的元素位置。我們用一幅圖來表示其原理。
這裡打一個比方,我們在dom元素中可能存在這樣的結構。
<view class="box" >
<view class="parent" >
<view class="son" > 這裡是將要繪製到canvas中的內容。</view>
</view>
</view>
對於上面的結構,我們只需要將 son中的內容繪製到 canvas 畫布中,那麼就有一個問題,我們要獲取哪一層級的元素信息(left,top,width,height),答案應該都能猜到,應該是想要繪製的內容最近的一層,也就是面的son層級。如果我們選外層,可能收到父元素padding,margin等影響,導致真實的位置不準確。
注意事項2: 儘量不要給獲取信息的元素增加 padding marign,如果繪製文本內容,儘量容器高度等於文本高度還有一個問題,就是儘量不要給需要繪製的元素,增加 padding marign等屬性,如果是繪製純文本,不要設置lineHeight,如圖下示例:
我們期望在獲取 a 點的位置信息, 但是最終卻獲取 b點的位置信息。如果用 b 點位置來繪製canvas,勢必不能完美還原設計稿,所以我們在用這種方式繪製canvas的時候,應該注意這些細節問題。
封裝獲取位置信息方法我們需要繪製海報上的每一個點位,首先想到的就是獲取小程序元素位置方法,並封裝該方法。我們用promise來防止深層次的回調,並且方便使用async await語法糖。廢話不多說,一言不合上代碼。
/* 獲取元素位置 */
geDomPostion(dom, isAll) {
return new Promise((resolve) => {
Taro.createSelectorQuery().select(dom).boundingClientRect(rect => {
const {
top,
left
} = rect
/* isAll 是否獲取設備寬高等信息 */
resolve(isAll ? rect : {
top,
left
})
}).exec()
})
},
小提示:如果用wx原生,或者其他跨端框架mpvue wepy uniapp是的同學,把 Taro 換成 wx 即可。
2 繪製網絡圖片繪製網絡圖片接下來我們要解決的問題: ⑨ 如何繪製網絡的圖片,兩種通過canvas畫布api,繪製圖片有什麼區別?
我們在用canvas繪製圖片的時候,對於本地圖片可以直接通過canvas提供的drawImage進行繪製,但是對於網絡圖片是不能這麼繪製的,我們首先需要通過getImageInfo來獲取圖片的臨時路徑。用getImageInfo繪製網絡資源的時候請注意配置一下合法的下載域名,要不然我們是無法成功獲取圖片信息的。我們首先需要在小程序後臺配置downloadFile合法域名。
具體步驟如下:第一步:
第二步:
第三步:
接下來我們要做的就是讀取圖片的臨時路徑,繪製到canvas畫布上來。
/* backGroundImageUrl 是我們要畫的網絡圖片的地址 */
this.getImageInfo(this.backGroundImageUrl).then(res=>{
const {
width, /* 寬度 */
height,
path /* 臨時路徑 */
} = res1
/* 第二步: 繪製banner圖 */
const bannerImage = await this.geDomPostion('#bannerImage')
this.startTop = bannerImage.top - 30
this.drawImage(context, node, path, 0, 0, width, height, 0, this.startTop, windowWidth, windowWidth)
context.save()
})
this.drawImage 是我們封裝好的方法,之前說過對於小程序獲取 context兩種接口方式,兩種方式繪製canvas圖片,有一些差別,我們馬上道來。
新老接口繪製圖片的區別老版本繪製方法
老版本api createCanvasContext可以直接使用 drawImage繪製圖片。如下
/* 繪製圖片 */
context.drawImage(url,x,y,width,height,dx,dy,dwidth,dheight)
當時我們項目用的是第二種新api getContext當時獲取上下文,所以在圖片繪製方式上,會有所改變。
新版本繪製方法
const image = node.createImage()
image.src = url
image.onload = () => {
context.drawImage(image,x,y,width,height,dx,dy,dwidth,dheight)
}
用新版本的API 繪製圖片的同學請注意,這個onload回調是在圖片加載完成時候執行的,所以說明是異步的。還有一個注意的地方,相比老版本的 drawImage 第一個參數是圖片的路徑,而新版本的drawImage第一個參數是image元素。
封裝繪製圖片方法
剛才在繪製網絡圖片最後一步,我們調用了 this.drawImage 方法。因為整個海報生成過程中,內部會畫入多張圖片,所以我們單獨封裝了一個繪製圖片的方法。
/* 繪製圖片 */
drawImage(context, node, url, ...arg) {
return new Promise((resolve) => {
const image = node.createImage()
image.src = url
image.onload = () => {
context.drawImage(image, ...arg)
resolve()
}
})
},
這樣我們就可以通過,async,await判斷圖片是否加載完成。
簡介 context.drawImage我這裡簡單給大家介紹一下context.drawImage用法,
CanvasContext.drawImage(imageResource / dom, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
繪製圖像到畫布,第一個參數,在老api中代表路徑,在新版本api中代表imagDom元素,
sx 需要繪製到畫布中的,imageResource / dom 的矩形(裁剪)選擇框的左上角 x 坐標
sy 需要繪製到畫布中的,imageResource / dom 的矩形(裁剪)選擇框的左上角 y 坐標
sWidth 需要繪製到畫布中的,imageResource / dom 的矩形(裁剪)選擇框的寬度
sHeight 需要繪製到畫布中的,imageResource / dom 的矩形(裁剪)選擇框的高度
dx imageResource的左上角在目標 canvas 上 x 軸的位置
dy imageResource的左上角在目標 canvas 上 y 軸的位置
dWidth 在目標畫布上繪製imageResource的寬度,允許對繪製的imageResource進行縮放
dHeight 在目標畫布上繪製imageResource的高度,允許對繪製的imageResource進行縮放
我們用一幅圖表示各個屬性的對應什麼。
解決問題: ④ canvas怎麼繪製疊在一起的兩張圖片,並控制層級?
如果我們繪製疊在一起的兩張圖片,需要我們做一些什麼樣的工作呢?首先想到的是層級問題,我們期望背景圖片放在下面,例如頭像之類的圖片放在上面,但是在畫布中沒有控制zIndex層級的屬性,那麼怎麼樣處理這個問題呢 ?答案是實際在canvas中,繪製的先後順序 就是畫布層級順序,後畫的在先畫的上層,那麼對於這種層級問題呢,我們只要保證層級高的元素後畫,層級低的元素先畫就可以完美解決,接下來我們在海報中,畫上頭像,文字等信息。
<!-- 頭像區 -->
<image class="userheadImage" id="userheadImage" :src="headImage" />
/*TODO: 繪製頭像 */
const userheadImage = await this.geDomPostion('#userheadImage',true)
/* 圓形圖片 */
let d = userheadImage.height / 2
const cx = userheadImage.left + userheadImage.width / 2
let cy = userheadImage.top + userheadImage.height / 2
context.arc(cx, cy, d, 0, 2 * Math.PI)
context.strokeStyle = '#FFFFFF'
context.stroke()
context.clip()
await this.drawImage(context, node, this.headImage, userheadImage.left, userheadImage.top, userheadImage.width, userheadImage.height)
context.restore()
this.drawText(context,{ top: userheadImage.top + userheadImage.height + 40 ,left : userheadImage.left - 70 },'我不是外星人「前端Sharing」',18,'normal 600 20px PingFangSC-Regular','#fff')
在我們使用context.clip()之後,記得使用context.restore()重置,否則將無法繪製其他元素。
效果:
我們完美解決了片文本的層級問題,接下來,我們就要繪製海報的主要的內容了。在我們繪製海報的時候,可能會遇到多行文本的情況,那麼多對多行文本,我們是怎麼解決的呢?
4 繪製多行文本解決問題:⑤ 如何用canvas繪製,多行文本?
canvas畫的文本,並不能像我們的dom元素下的文本一樣,可以自動換行,我們如何還原,多行文本的效果呢。這這裡教大家一種方法,我們可以一個一個字的繪製到canvas中,然後把每個字的寬度相加,如果總寬度大於容器的寬度,那麼就另外起一行,增加每一行的高度,從頭開始畫。,我們直接上代碼。
/** 畫多行文本
* @param ctx canvas 上下文
* @param str 多行文本
* @param initHeight 容器初始 top值
* @param initWidth 容器初始 left值
* @param canvasWidth 容器寬度
*/
drawRanksTexts(ctx, str, initHeight, initWidth, canvasWidth) {
let lineWidth = 0;
let lastSubStrIndex = 0;
/* 設置文字樣式 */
ctx.fillStyle = "#303133"
ctx.font = 'normal 400 15px PingFangSC-Regular'
for (let i = 0; i < str.length; i++) {
lineWidth += ctx.measureText(str[i]).width
if (lineWidth > canvasWidth) { /* 換行 */
ctx.fillText(str.substring(lastSubStrIndex, i), initWidth, initHeight)
initHeight += 20
lineWidth = 0
lastSubStrIndex = i
}
if (i == str.length - 1) { /* 無需換行 */
ctx.fillText(str.substring(lastSubStrIndex, i + 1), initWidth, initHeight)
}
}
},
調用
/* TODO: 複製多行文本 */
const rowsText = await this.geDomPostion('#context', true)
this.drawRanksTexts(context, this.skuName, rowsText.top, rowsText.left, rowsText.width)
效果
接下來我們做的是繪製二維碼,繪製二維碼過程,筆者踩了不少的坑,尤其taro-vue不支持createCanvasContext方式,希望我能用自己踩的坑,讓大家避開相同的錯誤,避免大家少走很多彎路。繪製二維碼實際並沒有想像的複雜,實際就是將連結轉換成二維碼,讓手機掃碼或者長按可以識別即可,雖然原理很簡單,但是還是有很多注意的細節。
繪製二維碼無異於二種方式,第一種方式就是用canvas畫出來。第二種將連結轉成base64的連結,然後讓圖片展示連結。 接下來我們針對這兩種方式,進行二維碼庫的技術選型。
1 關於二維碼庫選型解決問題 ⑨ 如何正確選型生成二維碼工具?
形成二維碼的過程,我們肯定不能手擼算法,因為即便我們能手擼出來,也會佔用大量時間,還會有很多bug,因為現在生成二維碼的生態已經很健全了,比如 qrcode.js等等都是非常不錯的,但是唯一不好的是不支持小程序端。我這裡介紹幾個二維碼的庫
weapp-qrcode對於比如短連結比如 筆者的 GitHub https://github.com/GoodLuckAlien 或者 掘金首頁 https://juejin.cn/user/2418581313687390 這種 ,都屬於短連結,不必拼寫很長的參數,這種情況用 weapp-qrcode 綽綽有餘。這種方式是基於第一種用canvas繪製的。而且是採用老版本的api , 這樣的話就有一個問題,如果像用新的 getContext 方式,就需要把源碼下載下來,然後改動一下源碼,讓它支持 getContext 這種方式。我們來簡介一下 weapp-qrcode的使用。
使用
// 將 dist 目錄下,weapp.qrcode.esm.js 複製到項目目錄中 如果用 taro uniapp 等框架 ,可以用 npm install
import drawQrcode from '../../utils/weapp.qrcode.esm.js'
drawQrcode({
width: 200,
height: 200,
canvasId: 'myQrcode',
// ctx: wx.createCanvasContext('myQrcode'),
text: 'https://juejin.cn/user/2418581313687390',
// v1.0.0+版本支持在二維碼上繪製圖片
image: {
imageResource: '../../images/icon.png',
dx: 70,
dy: 70,
dWidth: 60,
dHeight: 60
}
})
結果
這種方式下,最後確實成功了,因為在做demo的時候,我用的是github短連結。但是一回歸筆者公司的項目,很長的連結,奈何生成的二維碼特別密集,手機根本識別不出來,無奈前功盡棄了,只能換其他的技術方案,所以筆者選擇了第二種比較穩的方式,形成base64文件。
qrcode-base64qrcode-base64 是將二維碼的連結,轉成base64的連結,並把這個連結作為src屬性賦值給圖片。我們先介紹一下基本用法。
下載
npm install qrcode-base64
使用
import QR from 'qrcode-base64'
var imgData = QR.drawImg(this.data.codeText, {
typeNumber: 4,
errorCorrectLevel: 'M',
size: 500
})
// 返回輸出base64編碼imgData
如上述代碼塊所示,imgData就是生成的base64連結,我們可以直接把它作為圖片的src,然後讓canvas將圖片繪製到我們的海報中去,但是又來了一個問題,canvas是不支持繪製base64的連結圖片的,真機上沒有任何效果,真實一步十個坑啊,我們還得想辦法解決這個問題。
2 canvas 繪製 base64圖片解決問題 ⑦ canvas怎麼繪製base64的圖片
對於上面說到的canvas不支持base64的圖片,那麼我們還要把二維碼繪製到海報中,那麼並不是沒有辦法,我們可以用小程序提供的文件系統來解決問題。
小程序文件系統wx.getFileSystemManager 獲取全局唯一的文件管理器,返回值 類似於node中的fs.
writeFile 寫入文件,可以將圖片寫入系統中。
const fs = wx.getFileSystemManager()
fs.writeFile(/* 寫入文件 */)
封裝繪製二維碼方法
/* 生成二維碼 */
drawCode(ctx, node, x, y) {
return new Promise((resolve) => {
const codeImageWidth = 150 /* 繪製二維碼寬度 */
const canvasImageWidth = 85 /* 二維碼繪製到canvas的寬度 */
const left = x - 15 /* left 值 */
const top = y - 22 /* top 值 */
const LogoWidth = 15 /* 二維碼logo寬度 */
const url = 'https://juejin.cn/user/2418581313687390'
const base64 = QR.drawImg(url, {
typeNumber: 4,
errorCorrectLevel: 'L',
size: codeImageWidth
})
/* 創建讀寫流 */
const fs = Taro.getFileSystemManager()
const times = new Date().getTime()
const codeimg = Taro.env.USER_DATA_PATH + '/' + times + '.png'
/* 將base64圖片寫入 */
fs.writeFile({
filePath: codeimg,
data: base64.slice(22), /* 數據流 */
encoding: 'base64',
success: async () => {
const offset = (canvasImageWidth - LogoWidth) / 2 /* 偏移量 */
/* 繪製圖片 */
await this.drawImage(ctx, node, codeimg, 0, 0, codeImageWidth, codeImageWidth, left, top, canvasImageWidth, canvasImageWidth)
await this.drawImage(ctx, node, this.logoUrl, left + offset, top + offset, LogoWidth, LogoWidth)
resolve()
}
})
})
},
如上所示我們完成了二維碼的繪製。讓我們來看一下如何使用。
使用
我們在wxml上寫一個元素,作為佔位,方便我們可以獲取二維碼的位置。
<view id="qrCode" class="store-uscode" />
/*TODO: 第四步:繪製二維碼 */
const qrCode = await this.geDomPostion('#qrCode')
await this.drawCode(context, node, qrCode.left - 20, qrCode.top - this.cavnsOffsetop)
效果
掃描成功
解決問題:⑩ 生成的二維碼,識別不出來怎麼辦。
有的時候我們展示的二維碼比較小的時候,因為色塊太密,手機也會有無法識別的情況。那麼我們如何調整二維碼,有能讓頁面儘量高保真的還原設計稿呢,這裡教大家一個小技巧,可以去先去二維碼生成網站,先適配手機可以識別的最佳比例,避免識別不出來的情況。推薦網站:草料二維碼 : https://cli.im/ 我們可以在線調試二維碼的像素,和 logo的大小,直到調整出,能夠符合設計的最佳大小。
在線調整二維碼
微調整 有的時候,我們需要對二維碼大小進行微調整,我這裡建議在調試階段,建立起常量控制,並調整寫好調整方法或公式。這樣做的好處是,每當我們作出微調整的時候,不會影響因為當前調整而再計算,如下。
const codeImageWidth = 150 /* 繪製二維碼寬度 */
const canvasImageWidth = 85 /* 二維碼繪製到canvas的寬度 */
const left = x - 15 /* left 值 */
const top = y - 22 /* top 值 */
const LogoWidth = 15 /* 二維碼logo寬度 */
const offset = (canvasImageWidth - LogoWidth) / 2 /* 偏移量 */
我們已經跑完整個流程。就剩下最後一步,生成海報圖片,轉發圖片了。生成海報可以用微信小程序canvas中的canvasToTempFilePath生成圖片路徑,然後通過previewImage方法瀏覽圖片,瀏覽圖片時候就可以喚醒微信小程序的分享好友功能了。這裡有一點我們應該注意,就是要截取canvas的有效高度。
上代碼:
/* 生成海報 */
makePc(node) {
const {
startTop, /* 截取canvas畫布的頂部 */
endTop, /* 截取canvas畫布的底部 */
windowWidth /* 屏幕寬度 */
} = this
const _this = this
Taro.canvasToTempFilePath({
x: 0,
y: startTop,
width: windowWidth,
height: endTop - startTop,
destWidth: windowWidth * 3,
destHeight: (endTop - startTop) * 3,
canvas: node,
success: function (res) {
Taro.hideLoading()
Taro.previewImage({
urls: [res.tempFilePath]
})
}
})
}
還是回到最初的那個問題,在調用 canvasToTempFilePath 方法的時候,新老 api 傳遞的參數不同。
在老版本API中 ,通過createCanvasContext 方式繪製的canvas ,canvasToTempFilePath 的配置屬性canvas, 微信開發者文檔是這麼解釋的 canvas 畫布標識,傳入 canvas 組件實例 (canvas type="2d" 時使用該屬性), 也就是canvas上下文context。
但是我們用的是新版本 ,通過 getContext 方式繪製的canvas ,當我們傳入的是context,竟然沒有效果,what? 還有這種事,難道是微信開發者文檔出現了問題嗎?後來發現在這種方式下,傳入的是通過 query.select獲取的canvas 的node節點,真是坑不少啊~~~。一口老血都要噴出來了
5 效果總覽五 總結在做這個功能的時候,真是遇到了很多坑,甚至於有一種欲哭無淚的感覺,不過踩著坑一路走來,確實也收穫蠻多。
項目地址我把整個生成海報的全流程的demo項目,上傳到github, 感興趣的同學,可以看看,尤其是正在做這個功能的同學們,加油~
項目地址 :
馬上快大年三十啦,提前給大家拜個年,祝大家新的一年心想事成,萬事如意。
如果你覺得這篇內容對你挺有啟發,我想邀請你幫我三個小忙:
點個「在看」,讓更多的人也能看到這篇內容(喜歡不點在看,都是耍流氓 -_-)歡迎加我微信「TH0000666」一起交流學習...關注公眾號「前端Sharing」,持續為你推送精選好文。