- 小程序是什么?
- 腾讯微信端的类原生app开发,用一堆腾讯写的组件和自己造一些组件,进行app开发吧。
- 第一个小程序
- 之前,自己在下班之后和周末的时候,敲代码吧。搞了一阵子的小程序,其实就是做了个音乐播放器Music Player,这个播放器最关键的是歌词的加载吧。毕竟都是要进行计算才可以的。
- 进公司的第一个小程序
- 好吧,也不是一个完整的小程序,就其中的一部分,写了几个组件。然后2018,1,22上线了。
- 介绍之后,开始说自己的踩坑之际
- 名片管理:原理很简单,用户输入信息和上传头像到名片模板中,形成个人名片。在点击按钮,进行下载。名片就保存到相册中了。
- 坑:后台不能单独地传一张含有用户信息地名片的链接,让我进行调用下载。原因是服务器难做。好吧,毕竟不是专业后台,我也说不了什么。所以名片下载的功能由我来开发。进公司看了一个礼拜的文档,然后接手。心里有点忐忑吧,毕竟后台搞不定。然后在接手的时候,就说我试试看一两天行不行,不行就算了。原因你懂的。毕竟用canvas进行画图,在形成图片,这行不行我也不知道。动手撸起来。
- 还是看文档,看到文档中的canvas部分,和进行google来问问看别人怎么做的。
- 过程,发现自己的表达和书写能力还是不行啊。还是写代码吧
- 调用wx.createCanvasContext(canvasId, this),形成画布,记得要保存执行上下文的统一,即this,
同时要保存画布大小跟屏幕一致
setPixel() {
const {
windowWidth,
windowHeight
} = wx.getSystemInfoSync();
// 设置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();
},
- 调用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("图片获取失败");
},
});
});
},
- 调用canvasContext.fillText进行文字书写。
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);
});
},
- 然后画好了,开始要导出来。这个时候记得使用jpg格式,因为png,导出来是背景透明的。使用的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: "导出图片失败,请稍后在尝试"
});
rej("导出图片失败,请稍后在尝试");
}
});
});
},
- 在调用wx.saveImageToPhotosAlbum(OBJECT),将图片保存到手机中去。(当图片为竖版的时候,导出的时候会变大,需要在相册中对图片先用手放大,然后在缩小,这样就显示正常了,本来想多加些空白距离,但是设计稿。。所以没有办法了。
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: "名片导出失败,请稍后在尝试"
})
});
},
- 使用了promise和async和await。毕竟要画名片,肯定要知道相关信息和图片资源。在这里写的时候,要将图片资源先下载下来,在进行绘制。使用的api是wx.downloadFile,这样得到图片资源,然后使用wx.getImageInfo得到图片的信息,主要宽高,因为说说是画上去,其实还是算上去的。(考虑到rpx和px的转化,还得写两个转化函数),毕竟有些api要求的单位不一样。。。贼烦的换算。。记得我们的名片要得到信息才能输出名片,所以都是在调用接口,和我不知道微信哪些方法是异步的。毕竟文档没有提,然后就使用async,用promise包装一下绘制的操作。等到所有的绘制完成,返回文件地址,进行下载。(不得不说promise真的强大)
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]);
...差不多就这样
- 竖版好画,横板难画,空间的换算。好吧,我承认自己有点辣鸡。。。算了大半天才换算对的。。还是要多计算啊。这个不好说。看官自己去换算吧。
坑:对的,记得有时候一行信息来自两段文字,所以要画的时候,考虑到前一段文字对后一段文字的影响,然后又要加个函数
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());
// 计算文字距离 去掉空格和|所占据的空间
console.log('length',text.length,'ddd',this.pxToRpx((text.length - 2) * arr));
resolve(this.pxToRpx((text.length - 2) * arr));
});
},
// 函数调用
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,防止一次出错,画布崩溃。
- 有时候我们习惯写color:red;但是微信没有这样的api啊。所以使用ctx.fillStyle('color'),ctx,fillRect(x,y,w,h)来进行文字的上色
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);
});
},
- 画布中无法对字体进行font-weight,所以 多次绘制文字,注意距离。
- 还有就是说好的vue的计算属性可以使用呢?然后被大佬吐槽死了,还是在进行复杂运算的时候进行使用吧。有时候自己真的手贱。真的怎么爽怎么来。。。然后今天一堆bug,要上线来一堆bug。mmp。
- 所有的元素最好都要包裹在一个元素中去。// vue的语法。😔有时候切不回来是真的麻瓜。
- 转发功能,怎么说呢,写慢点。主要看同事都在等自己,才能下班。心急,打错大小写。。
- 还有就是微信的wx.request的get请求还是按照格式来,不然小程序会对其中进行改变,然后出错。// 公司的编译器在智能点就无敌了。// 虽然看了部分代码。还是要多学习啊
- 忘记最难的是画头像,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);
}
});
});
},
- 然后在绘制过程中,要考虑到两段文字要显示在一行。(昨天在随便玩的时候发现的),后一段文字的位置是根据前一段文字来显示的,不是固定的。所以又要计算,微信有个api是设置字体大小的canvasContext.setFontSize,但是应该是px,没有仔细研究过。毕竟一堆报错等我去解决。幸好有同事的帮忙,不然就炸了。这个时候,又是rpx跟px的换算。然后发现数字跟文字显示不一样。原因待我更新。解决了,看上面的7中的横版。
- 少发请求。今天有一个函数写的不好。真的,是因为,当时没有考虑太多,是按照文档的思路写下来的。然后自己写完,丢测试,看看有没有完美实现功能,就没有考虑太多的性能优化的事情。好吧,有考虑到,但是是考虑使用debounce,strolltetimg这样的函数去完成。。然后在同事的提醒下,发现写法不行。虽然自己总结了性能优化的文章,但是哎还是实践的少啊。
Update:要实时响应的。mmp。被人怼的时候,就忘记了。坚持主见啊。 - 幸好今天能上线了。算是自己第一个正式的小程序吧,虽然只是一部分。
- 明天还是要修前同事的bug,和看rxjs。rxjs学习好难。。。。。。
- 函数式编程看了一些,不得不承认,看懂的少啊,还是要多滚文档啊。react全家桶去实现vue社区2.0还没有做完啊。
- 今天碰到的bug都是自己编程过程中第一次碰见的。vue:the infinite update loop 的问题,少了个整个包裹的div。
- style 要加scope。 同时当样式显示不对的时候,要考虑到是不是样式冲突了。今天是真的急啊。谁知道上线的今天会出现这么多bug。。。
- 幸好四阿哥,哈哈
Update 1.25
- 好吧,昨天又遇见坑。客户反应名片下载不下来,测试反馈给我,我当然火急火燎地去修bug了。不修还好,一修理就感觉代码逻辑不满意,然后重写代码过程,其中发现自己对promise的特性还是使用不行,简单而言,没有完全搞懂。
- Promise 是一种承诺,是一种面对未来发现而进行的提前编程。一共有三种状态:padding,resolve,reject。其中对reject的使用不当,其reject出来的东西,应该是被catch所捕捉到的,这点在昨天的编程中忘记了。。。简直麻瓜,
- 有时候一份代码反复编写测试,有问题在修改,就自己而言是很难受的,这就要求自己要在编写过程中更多的思考,但是自己基本提交测试的代码是自己写的满意才提交的,但是还有问题。这就说明,就小程序而言,我文档还是啃得不熟悉,然后继续啃文档,多啃小程序上线的配置代码。
- 流程问题,往往写完要给发布测试环境的人员,然后其在转发给正式发布人员,然后测试人员进行测试,然后问题在反馈给我。一个小问题,然后就倒霉了,以前都是自己搞得,所以改起来快,现在只能一步步来,一些小问题不得不让自己烦躁,毕竟一遍遍地滚流程是对自己的折磨,自我感觉。
- 结论:重写所有的逻辑,然后线上还是报错。当时崩溃,然后测试说有可能token失效,然后大佬问起来我canvas是要下载画图的,告诉我有可能客户的域名没有配置。现在坐等结果。
Update 1.27
问了测试,客户没有说,测试那就说ok,那就ok吧。。。蛋疼,也不知道ok不ok
Update 2019.5.22
在修改别人的小程序的过程中,发现一个现象。
Page({
/**
* 页面的初始数据
*/
data: {
page:0},
})
handleClick(e){
this.data.page+=1;} // 这里会直接改变data中的page,而不是通过this.setData({page:this.data.page+1})来改变,这个刚看见的时候我还以为是错的。现在才发现是可以的,还是too young了。然后翻下小程序论坛,说setData是唯一的视图和数据的通信接口。毕竟是数据驱动视图