做了一些小程序項(xiàng)目,著重記錄一下小程序內(nèi)嵌webview以及小程序畫canvas遇到的一些坑。
小程序畫canvas的坑
第一個坑,canvas無法畫base64格式的二維碼。
需求:前端將一些動態(tài)數(shù)據(jù),以及動態(tài)生成的小程序二維碼圖片用canvas畫出來,最后生成且可保存的一張圖片。
前端顯示二維碼圖片一般有兩種方法:
1.后端調(diào)用微信api,直接返回的二進(jìn)制數(shù)據(jù)到前端會亂碼,一般后端先將二進(jìn)制數(shù)據(jù)轉(zhuǎn)為base64格式數(shù)據(jù),然后返回到前端。若是當(dāng)普通的圖片顯示只需要設(shè)置圖片:
<image src="data:image/png;base64,圖片base64數(shù)據(jù)"></image>
2.后臺在服務(wù)器上將二進(jìn)制數(shù)據(jù)保存為一張圖片,然后返回給前端一個二維碼的絕對路徑。
若后臺因?yàn)楦鞣N原因不好實(shí)現(xiàn)第2種,且第1種方法微信官方的回復(fù)是:真機(jī)上小程序的canvas無法繪制base64格式的圖片,難道就無法在canvas里繪制二維碼了?
此時還有第3種方法就是利用小程序的文件系統(tǒng) FileSystemManager.writeFile() Api,將base64數(shù)據(jù)的圖片寫入到本地,之后去操作這個本地的圖片。
第一步創(chuàng)建寫入文件的文件夾
mkdir() {
return new Promise((resolve, reject) => {
this.fs.access({ // 先判斷有沒有resource目錄
path: `${wx.env.USER_DATA_PATH}/resource`, // wx.env.USER_DATA_PATH獲取本地文件路徑,開發(fā)者工具是:"http://usr",真機(jī)是:"wxfile://usr"
success: (res) => {
console.log('access:',res)
resolve(res)
},
fail: (err) => {
console.error('access:',err)
// 不存在,創(chuàng)建一個文件夾
this.fs.mkdir({
dirPath: `${wx.env.USER_DATA_PATH}/resource`,
success: (res) => {
console.log('mkdir:', res)
resolve(res)
},
fail: (err) => {
console.error(err)
reject(res)
}
})
}
})
})
}
復(fù)制代碼
第二步將base64圖片寫入創(chuàng)建的文件夾
writeBase64Image() {
return this.mkdir().then(() => {
return new Promise((resolve, reject) => {
this.fs.writeFile({
filePath: `${wx.env.USER_DATA_PATH}/resource/${this.openId}.png`, // 可以用openId來命名此圖片
data: this.base64Code, //后臺返回的base64編碼的圖片,不需要加'data:image/png;base64,'
encoding: 'base64', // 存的文件類型的選擇base64
success:(res) => {
console.log('writeFile:', res)
resolve(res)
},
fail:(err) => {
console.error('writeFile:',err)
reject(err)
}
})
})
})
}
復(fù)制代碼
第三步獲得圖片的緩存臨時地址
如果是絕對路徑的網(wǎng)絡(luò)圖片,繪制圖片前需要將圖片緩存到本地,如果是寫入的本地文件則可以直接繪制。
getImageSrc(src) {
return new Promise((resolve, reject) => {
// 如果是本地圖片則直接返回;如果是網(wǎng)絡(luò)路徑的圖片,則先緩存到本地,wx.getImageInfo或者wx.downloadFile獲得本地臨時路徑
!src.includes('https') ? resolve(src) :
wx.getImageInfo({
src,
success(res) {
resolve(res) // res.path:返回的臨時路徑
},
fail(err) {
reject(err)
}
})
})
},
復(fù)制代碼
之后通過獲得臨時文件路徑用canvas繪制圖片。
以前遇到這個問題,絕對大多數(shù)的開發(fā)者都是采用后端返回圖片絕對路徑給前端,而第3種則可以前端去做保存圖片的事,減少很多后端服務(wù)器的操作。
第二個坑,一些android機(jī)生成的canvas圖片,文字樣式混亂
尤其是華為P20,哈哈真真是bug神機(jī)了。 小程序的canvas組件是原生組件,原生組件脫離在 WebView 渲染流程外,對于頻繁繪制而機(jī)型又繪制較慢時,就很容易樣式錯亂,解決方法就是生成圖片時給一定時間的延遲。
// 在繪制canvas的回調(diào)里,導(dǎo)出生成的圖片,如果是andriod則延遲500ms生成。如果500ms還會有樣式問題,那就增加延遲時間。
ctx.draw(() => {
// some code
isAndroid && setTimeout(() => {
wx.canvasToTempFilePath({
// some code
})
},500)
// some code
})
復(fù)制代碼
第三個坑,在ios上只能繪制顯示的canvas元素,若設(shè)置wx:if或hidden,在初始化時必須是顯示的。
如果你想繪制一個隱藏的canvas元素(hidden=true),將這個canvas轉(zhuǎn)為圖片保存,android下正常,ios下則不會繪制canvas。
其他的一些問題
用css3的旋轉(zhuǎn)動畫,在真機(jī)上沒效果,最好使用wx.createAnimation代替;
canvas元素不能添加css動畫,會非常的卡,即使使用wx.createAnimation代替,也是很卡。這個官方文檔也有提到。
小程序內(nèi)嵌webview的一些坑
第一個坑,判斷是否在小程序環(huán)境內(nèi)
如果app的首頁以及一些其他頁面是服務(wù)端渲染,那么官方的提供的方法,并不一定能判斷準(zhǔn)確,尤其在android下。
如果想通過,navigator.userAgent里的miniprogram字符串判斷小程序,我勸你還是放棄吧....,真機(jī)下miniprogram字符串時有時無,官方的回復(fù)永遠(yuǎn)都是這是最近出現(xiàn)的bug,會在后續(xù)版本修復(fù)......
// 在服務(wù)端渲染的頁面下,下面的方法無法保證準(zhǔn)確的判斷,尤其在andriod下
function ready() {
console.log(window.__wxjs_environment === 'miniprogram') // true
}
if (!window.WeixinJSBridge || !WeixinJSBridge.invoke) {
document.addEventListener('WeixinJSBridgeReady', ready, false)
} else {
ready()
}
// 或者
wx.miniProgram.getEnv(function (res) {
console.log(res.miniprogram) // true
})
復(fù)制代碼
解決:1.將小程序標(biāo)識拼在web-view的src屬性上
<web-view src="https://XXXX.com?isMiniProgram=1"></web-view>
復(fù)制代碼
第二個坑,微信瀏覽器和小程序會共用localStorage
微信瀏覽器內(nèi)訪問的是正常的H5,小程序內(nèi)訪問的是內(nèi)嵌的webview下的H5,很多時候需要這倆個環(huán)境各有不同的數(shù)據(jù)緩存,區(qū)分功能。 而微信在某次更新后就開始共用微信瀏覽器和小程序的storage了,也是哭哭啊。。。。
解決:如果只是一個頁面打開的后續(xù)頁面間互相傳遞數(shù)據(jù),那么可以使用sessionStorage去臨時緩存數(shù)據(jù)而不用localStorage。