- 小程序是什么?
- 騰訊微信端的類原生app開發(fā),用一堆騰訊寫的組件和自己造一些組件,進(jìn)行app開發(fā)吧。
- 第一個小程序
- 之前,自己在下班之后和周末的時候,敲代碼吧。搞了一陣子的小程序,其實就是做了個音樂播放器Music Player,這個播放器最關(guān)鍵的是歌詞的加載吧。畢竟都是要進(jìn)行計算才可以的。
- 進(jìn)公司的第一個小程序
- 好吧,也不是一個完整的小程序,就其中的一部分,寫了幾個組件。然后2018,1,22上線了。
- 介紹之后,開始說自己的踩坑之際
- 名片管理:原理很簡單,用戶輸入信息和上傳頭像到名片模板中,形成個人名片。在點擊按鈕,進(jìn)行下載。名片就保存到相冊中了。
- 坑:后臺不能單獨地傳一張含有用戶信息地名片的鏈接,讓我進(jìn)行調(diào)用下載。原因是服務(wù)器難做。好吧,畢竟不是專業(yè)后臺,我也說不了什么。所以名片下載的功能由我來開發(fā)。進(jìn)公司看了一個禮拜的文檔,然后接手。心里有點忐忑吧,畢竟后臺搞不定。然后在接手的時候,就說我試試看一兩天行不行,不行就算了。原因你懂的。畢竟用canvas進(jìn)行畫圖,在形成圖片,這行不行我也不知道。動手?jǐn)]起來。
- 還是看文檔,看到文檔中的canvas部分,和進(jìn)行g(shù)oogle來問問看別人怎么做的。
- 過程,發(fā)現(xiàn)自己的表達(dá)和書寫能力還是不行啊。還是寫代碼吧
- 調(diào)用wx.createCanvasContext(canvasId, this),形成畫布,記得要保存執(zhí)行上下文的統(tǒng)一,即this,
同時要保存畫布大小跟屏幕一致
setPixel() {
const {
windowWidth,
windowHeight
} = wx.getSystemInfoSync();
// 設(shè)置canvas跟屏幕一樣大小
this.width = this.rpxToPx(700);
this.height = this.rpxToPx(1040);
const ctx = arguments[0];
ctx.setFillStyle("white");
ctx.fillRect(0, 0, this.width, this.height);
ctx.draw();
},
- 調(diào)用canvasContext.drawImage,貼圖片
downAndSetImage(ctx, url, arr) {
// arr =[x,y,width,height]
arr = this.rpxToPx(arr);
ctx.draw(true);
return new Promise((resolve, rej) => {
wx.downloadFile({
url: url,
success: function(res) {
let path = res.tempFilePath;
// ctx.drawImage(path, arr[0], arr[1], arr[2], arr[3]); //還是以px為單位的。//換算
ctx.save();
ctx.drawImage(
path,
arr.shift(),
arr.shift(),
arr.shift(),
arr.shift()
); //還是以px為單位的。//換算
resolve(1);
ctx.restore();
},
fail: () => {
ctx.save();
wx.showModal({
title: "提示",
content: `圖片獲取失敗`
});
ctx.restore();
rej("圖片獲取失敗");
},
});
});
},
- 調(diào)用canvasContext.fillText進(jìn)行文字書寫。
setText(ctx, text, arr) {
// arr :[x,y,font-size]
ctx.draw(true);
arr = this.rpxToPx(arr);
return new Promise((resolve, rej) => {
arr.length == 2 ? ctx.setFontSize(14) : ctx.setFontSize(arr.pop());
ctx.setFillStyle("black");
ctx.setTextBaseline("top");
ctx.fillText(text, arr.shift(), arr.shift());
resolve(3);
});
},
- 然后畫好了,開始要導(dǎo)出來。這個時候記得使用jpg格式,因為png,導(dǎo)出來是背景透明的。使用的api:wx.canvasToTempFilePath(OBJECT, this)
// output image path
outputImage() {
const ctx = arguments[0];
const that = this;
const {
windowWidth,
windowHeight,
pixelRatio
} = wx.getSystemInfoSync();
return new Promise((resolve, rej) => {
wx.canvasToTempFilePath({
x: 0,
y: 0,
// width: 50,
// height: 50,
destWidth: pixelRatio * windowWidth, //canvas width*pixelRatio
destHeight: pixelRatio * windowHeight,
canvasId: "firstCanvas",
fileType: "jpg",
success: function(res) {
resolve(res.tempFilePath);
},
fail: function() {
wx.showModal({
title: "提示",
content: "導(dǎo)出圖片失敗,請稍后在嘗試"
});
rej("導(dǎo)出圖片失敗,請稍后在嘗試");
}
});
});
},
- 在調(diào)用wx.saveImageToPhotosAlbum(OBJECT),將圖片保存到手機(jī)中去。(當(dāng)圖片為豎版的時候,導(dǎo)出的時候會變大,需要在相冊中對圖片先用手放大,然后在縮小,這樣就顯示正常了,本來想多加些空白距離,但是設(shè)計稿。。所以沒有辦法了。
saveCard() {
const that = this;
const {
windowWidth,
windowHeight,
pixelRatio
} = wx.getSystemInfoSync();
wx.canvasToTempFilePath({
destWidth: pixelRatio * windowWidth, //canvas width*pixelRatio
destHeight: pixelRatio * windowHeight,
canvasId: "firstCanvas",
fileType: "jpg",
success: function(res) {
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success(res) {
wx.showModal({
title: "提示",
content: "名片保存到相冊成功"
});
},
fail() {
wx.showModal({
title: "提示",
content: "名片保存失敗,請稍后在嘗試!"
});
},
complete() {
console.log("completed");
}
});
},
fail: () =>
wx.showModal({
title: "提示",
content: "名片導(dǎo)出失敗,請稍后在嘗試"
})
});
},
- 使用了promise和async和await。畢竟要畫名片,肯定要知道相關(guān)信息和圖片資源。在這里寫的時候,要將圖片資源先下載下來,在進(jìn)行繪制。使用的api是wx.downloadFile,這樣得到圖片資源,然后使用wx.getImageInfo得到圖片的信息,主要寬高,因為說說是畫上去,其實還是算上去的。(考慮到rpx和px的轉(zhuǎn)化,還得寫兩個轉(zhuǎn)化函數(shù)),畢竟有些api要求的單位不一樣。。。賊煩的換算。。記得我們的名片要得到信息才能輸出名片,所以都是在調(diào)用接口,和我不知道微信哪些方法是異步的。畢竟文檔沒有提,然后就使用async,用promise包裝一下繪制的操作。等到所有的繪制完成,返回文件地址,進(jìn)行下載。(不得不說promise真的強(qiáng)大)
async getCanvas() {
const arg = arguments[0];
await this.setPixel.call(null, arg);
// 顯示loading
wx.showLoading({
title: "繪制中,請稍等"
});
//code
await this.downAndSetImage(arg, this.code, [500, 111, 180, 180]);
//company
await this.setText(arg, this.userInfo.company.companyName, [33, 218]);
...差不多就這樣
- 豎版好畫,橫板難畫,空間的換算。好吧,我承認(rèn)自己有點辣雞。。。算了大半天才換算對的。。還是要多計算啊。這個不好說??垂僮约喝Q算吧。
坑:對的,記得有時候一行信息來自兩段文字,所以要畫的時候,考慮到前一段文字對后一段文字的影響,然后又要加個函數(shù)
dynamicSetTextColor(ctx, text, arr, color) {
// arr :[x,y,font-size]
arr = this.rpxToPx(arr);
return new Promise((resolve, rej) => {
!arr.length == 3 ? ctx.setFontSize(18) : ctx.setFontSize(arr[2]);
!color ? ctx.setFillStyle("black") : ctx.setFillStyle(color);
ctx.draw(true);
ctx.setTextBaseline("top");
ctx.fillText(text, arr.shift(), arr.shift());
// 計算文字距離 去掉空格和|所占據(jù)的空間
console.log('length',text.length,'ddd',this.pxToRpx((text.length - 2) * arr));
resolve(this.pxToRpx((text.length - 2) * arr));
});
},
// 函數(shù)調(diào)用
await this.dynamicSetTextColor(
arg,
`${this.userInfo.user.userName} |`, [469, 197, 40],
"#338BF5"
).then(r => {
// r是距離
that.setText(arg, that.userInfo.user.positionName, [r + 475, 205, 28]);
});
- 一定要使用draw(true),restore,防止一次出錯,畫布崩潰。
- 有時候我們習(xí)慣寫color:red;但是微信沒有這樣的api啊。所以使用ctx.fillStyle('color'),ctx,fillRect(x,y,w,h)來進(jìn)行文字的上色
setTextColor(ctx, text, arr, color) {
// arr :[x,y,font-size]
arr = this.rpxToPx(arr);
return new Promise((resolve, rej) => {
!arr.length == 3 ? ctx.setFontSize(18) : ctx.setFontSize(arr[2]);
!color ? ctx.setFillStyle("black") : ctx.setFillStyle(color);
ctx.draw(true);
ctx.setTextBaseline("top");
ctx.fillText(text, arr.shift(), arr.shift());
resolve(3);
});
},
- 畫布中無法對字體進(jìn)行font-weight,所以 多次繪制文字,注意距離。
- 還有就是說好的vue的計算屬性可以使用呢?然后被大佬吐槽死了,還是在進(jìn)行復(fù)雜運算的時候進(jìn)行使用吧。有時候自己真的手賤。真的怎么爽怎么來。。。然后今天一堆bug,要上線來一堆bug。mmp。
- 所有的元素最好都要包裹在一個元素中去。// vue的語法。??有時候切不回來是真的麻瓜。
- 轉(zhuǎn)發(fā)功能,怎么說呢,寫慢點。主要看同事都在等自己,才能下班。心急,打錯大小寫。。
- 還有就是微信的wx.request的get請求還是按照格式來,不然小程序會對其中進(jìn)行改變,然后出錯。// 公司的編譯器在智能點就無敵了。// 雖然看了部分代碼。還是要多學(xué)習(xí)啊
- 忘記最難的是畫頭像,mmp,傳的是正方形的頭像啊,要畫圓啊。還沒有border-radius:50%這樣的api使用。畫圓圈的思路:canvasContext.clip這個api,最難的是對中心點。具體的忘記了,明天在貼。因為google出來,別人也沒有告訴我怎么畫,都是自己琢磨出來的。。。
avatar(ctx, url, arr) {
arr = this.rpxToPx(arr);
ctx.draw(true);
return new Promise((resolve, rej) => {
wx.downloadFile({
url: url,
success: function(res) {
let path = res.tempFilePath;
const r = 30;
ctx.save();
ctx.beginPath();
ctx.arc(arr[0], arr[1], r, 0, 2 * Math.PI);
ctx.clip();
ctx.drawImage(path, arr[0] - r, arr[1] - r, arr[2] * 2, arr[3] * 2); //還是以px為單位的。//換算
ctx.restore();
resolve(1);
}
});
});
},
- 然后在繪制過程中,要考慮到兩段文字要顯示在一行。(昨天在隨便玩的時候發(fā)現(xiàn)的),后一段文字的位置是根據(jù)前一段文字來顯示的,不是固定的。所以又要計算,微信有個api是設(shè)置字體大小的canvasContext.setFontSize,但是應(yīng)該是px,沒有仔細(xì)研究過。畢竟一堆報錯等我去解決。幸好有同事的幫忙,不然就炸了。這個時候,又是rpx跟px的換算。然后發(fā)現(xiàn)數(shù)字跟文字顯示不一樣。原因待我更新。解決了,看上面的7中的橫版。
- 少發(fā)請求。今天有一個函數(shù)寫的不好。真的,是因為,當(dāng)時沒有考慮太多,是按照文檔的思路寫下來的。然后自己寫完,丟測試,看看有沒有完美實現(xiàn)功能,就沒有考慮太多的性能優(yōu)化的事情。好吧,有考慮到,但是是考慮使用debounce,strolltetimg這樣的函數(shù)去完成。。然后在同事的提醒下,發(fā)現(xiàn)寫法不行。雖然自己總結(jié)了性能優(yōu)化的文章,但是哎還是實踐的少啊。
Update:要實時響應(yīng)的。mmp。被人懟的時候,就忘記了。堅持主見啊。 - 幸好今天能上線了。算是自己第一個正式的小程序吧,雖然只是一部分。
- 明天還是要修前同事的bug,和看rxjs。rxjs學(xué)習(xí)好難。。。。。。
- 函數(shù)式編程看了一些,不得不承認(rèn),看懂的少啊,還是要多滾文檔啊。react全家桶去實現(xiàn)vue社區(qū)2.0還沒有做完啊。
- 今天碰到的bug都是自己編程過程中第一次碰見的。vue:the infinite update loop 的問題,少了個整個包裹的div。
- style 要加scope。 同時當(dāng)樣式顯示不對的時候,要考慮到是不是樣式?jīng)_突了。今天是真的急啊。誰知道上線的今天會出現(xiàn)這么多bug。。。
- 幸好四阿哥,哈哈
Update 1.25
- 好吧,昨天又遇見坑??蛻舴磻?yīng)名片下載不下來,測試反饋給我,我當(dāng)然火急火燎地去修bug了。不修還好,一修理就感覺代碼邏輯不滿意,然后重寫代碼過程,其中發(fā)現(xiàn)自己對promise的特性還是使用不行,簡單而言,沒有完全搞懂。
- Promise 是一種承諾,是一種面對未來發(fā)現(xiàn)而進(jìn)行的提前編程。一共有三種狀態(tài):padding,resolve,reject。其中對reject的使用不當(dāng),其reject出來的東西,應(yīng)該是被catch所捕捉到的,這點在昨天的編程中忘記了。。。簡直麻瓜,
- 有時候一份代碼反復(fù)編寫測試,有問題在修改,就自己而言是很難受的,這就要求自己要在編寫過程中更多的思考,但是自己基本提交測試的代碼是自己寫的滿意才提交的,但是還有問題。這就說明,就小程序而言,我文檔還是啃得不熟悉,然后繼續(xù)啃文檔,多啃小程序上線的配置代碼。
- 流程問題,往往寫完要給發(fā)布測試環(huán)境的人員,然后其在轉(zhuǎn)發(fā)給正式發(fā)布人員,然后測試人員進(jìn)行測試,然后問題在反饋給我。一個小問題,然后就倒霉了,以前都是自己搞得,所以改起來快,現(xiàn)在只能一步步來,一些小問題不得不讓自己煩躁,畢竟一遍遍地滾流程是對自己的折磨,自我感覺。
- 結(jié)論:重寫所有的邏輯,然后線上還是報錯。當(dāng)時崩潰,然后測試說有可能token失效,然后大佬問起來我canvas是要下載畫圖的,告訴我有可能客戶的域名沒有配置。現(xiàn)在坐等結(jié)果。
Update 1.27
問了測試,客戶沒有說,測試那就說ok,那就ok吧。。。蛋疼,也不知道ok不ok
Update 2019.5.22
在修改別人的小程序的過程中,發(fā)現(xiàn)一個現(xiàn)象。
Page({
/**
* 頁面的初始數(shù)據(jù)
*/
data: {
page:0},
})
handleClick(e){
this.data.page+=1;} // 這里會直接改變data中的page,而不是通過this.setData({page:this.data.page+1})來改變,這個剛看見的時候我還以為是錯的?,F(xiàn)在才發(fā)現(xiàn)是可以的,還是too young了。然后翻下小程序論壇,說setData是唯一的視圖和數(shù)據(jù)的通信接口。畢竟是數(shù)據(jù)驅(qū)動視圖