对于微信小程序开发入门,还是比较简单的,只需要具备基本的css+js知识就可以了,成本比较低。 写了小程序和RN之后,有一种原生很笨重的感觉,就是小程序或者是RN等这些新的开发方式在效率上面真的有比较大的优势,唯一不足就是运行速度了(使用Canvas就会有这样子的感觉)。 感觉目前所接触的种类前端开发(包括移动端),都是基本一个套路:UI,网络,数据保存,富文本,图片/视频。 本文也是从这几个方向去总结自己的小程序开发经验。
小程序的入门
其实小程序的开发过程一直都是查看文档,按照文档去操作就可以了。 一般流程是先看简易教程。看完之后,再去看组件。之后可以开始尝试写需求,这个过程中,开始不断的去查API和框架即可。
多列列表
在开发中,有一个需求是需要实现类似Android的GridView网格列表的。但是微信中并没有提供这样子的组件,但是小程序是跟html/css前端很类似的,他可以通过指定 display:flex
,然后去设置 flex-wrap:wrap
就可以。例如,有一个数组 data:["A","B","C","D","E","F","G","H","I","J","K","L","M","N"]
需要显示为一个三列的列表,可以如下处理:
//GridPage.wxml<view class='grid-container'> <view wx:for="{{data}}" wx:key="{{item}}" class="grid-list"> <view class='grid-item'> <text class='grid-item-text'>{{item}}</text> </view> </view></view>//GridPage.wxssPage { min-height: 100%; background-color: #fff;}.grid-container { margin-left: 4rpx; margin-right: 4rpx; display: flex; flex-wrap: wrap; flex-direction: row;}.grid-list { width: 33.33%;}.grid-item { margin: 2rpx; background: #999; display: flex; justify-content: center; align-items: center;}.grid-item-text { color: black;}
这里的重点就是 grid-container
中的 flex-wrap
为 wrap
,方向是 row
了。然后他的每一个item宽度都是 33.33%
。需要注意的是一定是去设置外部的contanier而不是内部的list。
层级布局
在CSS中,需要使用层级布局,就是类似Android的FrameLayout效果,可以使用z-index,也可以使用一个绝对定位。比如,我们有一个需求是:下面是一个图片,上面是文字。
//PositionPage.wxml<view class='root'><image src='https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=3756982450,995202616&fm=27&gp=0.jpg' class='image'></image><text class='text'>我是权律二啊</text></view>//PositionPage.wcss.root{ align-items: center; display: flex; flex-direction: column; position: relative;}.image{ width: 300rpx; height: 300rpx;}.text{ background-color: #999; position: absolute;}
主要是两点:父布局的 position
必须是 relative
,它本身 position
必须是 absolute
。
网络请求
小程序的网络请求是使用wx.request()方法,但是该方法太臃肿,并没有使用Promise那样子简洁。幸运的是小程序支持Promise,所以我们可以把http封装一下,变成有条理。说到这里,大家做的时候需要注意去微信后台配置各种request域名,upload域名,downloadFIle域名。 下面封装的例子的数据返回格式都是json格式post请求方式发出的:
//真正发起请求function _request(url, param) { if (isDebug) { Log.i("http==> params->" + JSON.stringify(param)); Log.i("http==> url->" + url); } return new Promise((resolve, reject) => { wx.request({ url: url, data: param, header: { 'content-type': 'application/json', "Accept": "application/json" }, method: "POST", success: function (response) { if (isDebug) { const jsonResponse = JSON.stringify(response); Log.i("http==> response->" + jsonResponse); } const {data, statusCode, ok = false} = response; //只有ok为true的是时候才返回成功,data不一定是包含数据的 if (statusCode === 200 && data && data.ok) { resolve(data, ok); } else { if (statusCode != 404 && statusCode < 500 && statusCode > 300) { ToastUtil.showError(); } reject(data); } }, fail: reject }); });}
使用:
function getInfo(fid) { const params = {}; params.token = user.token; params.uid = user.uid; return _request("INFO_URL", params);}
然后需要发起请求就调用该方法即可,处理Promise。
上传图片到阿里云
需要注意微信upload接口配置目前好像不可以直接配置阿里云的URL,需要阿里云先 跟我们的域名绑定,之后再去把设置到微信后台的upload接口中。可以参考博客: 小程序图片上传阿里OSS使用方法 ,获取签名阿里云Demo地址: JavaScript客户端签名直传 ,通过打log获得policy和signature(签名时间可以稍微设置久一点)之后,就开始封装upload方法了。 如下:
/** * 真实上传代码 */ function _upload(file, success, fail) { const suffix = file.substring(file.lastIndexOf(".")); //做一下md5处理 const fileName = hex_md5(file); Log.i("fileName=" + (fileName + suffix)); wx.uploadFile({ url: ALIYUNPHOTOADDRESS, formData: { "OSSAccessKeyId": "你的阿里云accessKey", "key": DIR+ (fileName + suffix), "policy": "你的policy", "success_action_status": '200', "signature": "你的signature" }, filePath: file, name: 'file', success: function (res) { const {statusCode} = res; if (statusCode === 200) { console.log(JSON.stringify(res)); success("" + fileName + suffix); } else { console.log("上传失败"); fail(res); } }, fail: function (e) { fail(e); console.log("上传失败"); console.log("e=" + JSON.stringify(e)); }, complete: function () { console.log("上传过程结束"); } }) }}
其中 url
是上传OOS的地址,key是需要上传的文件夹+上传之后的文件名。这里的fileName我们通过一个md5去计算得来,保证唯一性又没有什么特殊字符。md5的算法来自JS-MD5加密。
我们可以顺带封装一个上传多张图片的方法,而且使用Promise返回:
/*** files需要上传的文件,是一个数组,里面是文件的绝对路径*/function uploadFiles(files) { if (!files || files.length <= 0) { wx.showModal({ title: '图片错误', content: '请重试', showCancel: false, }); return Promise.reject(); } Log.i("开始上传" + files); return new Promise((resolve, reject) => { //上传成功的文件名称 let uploadPaths = []; for (let i = 0; i < files.length; i++) { _upload(files[i], (path) => { //成功的文件名 uploadPaths[uploadPaths.length] = path; if (uploadPaths.length >= files.length) { //把url+name返回 resolve([ALIYUNPHOTOADDRESS + "/" +DIR, uploadPaths]); } }, () => { //error reject(res); }); } });
Canvas使用
- 由于需要使用Canvas画一棵树,所以还是在这里走了比较多的坑的。我的需求是Canvas全屏,除了画一个树之外还需要画别的一些独立Button。 首先,设置Canvas全屏和不可滑动,可以通过以下方式:
<canvas disable-scroll='true' style="width: {{width}}px; height: {{height}}px;background-color:#efeff4;flex:1;" canvas-id="canvas" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd"></canvas>
- 其中,在设置了disable-scroll设置为true,同时需要绑定三个touch事件,才能响应画布的触摸event。其中这里的width+height是通过wx.getSystemInfo()获得。
- 其次,canvas没有类似View的catchtap事件,只有一些touch事件,详情可以看Canvas
- 然后在微信小程序中Canvas是层级最高的,无法通过设置z-index去调,所以假如你的Canvas全屏,还需要一些其他的Button,那么只能通过最后canvas去draw了。
- 在canvas中,假如通过moveTo+lineTo去画线,一般需要先调用canvas.beginPath()画完成之后,先调用canvas.stoke(),然后在调用canvas.closePath();
c.beginPath(); c.setLineWidth(this.arrowPaint.width); c.setStrokeStyle(this.arrowPaint.color); c.setLineCap("square"); c.moveTo(this.arrowStartPointF.x, this.arrowStartPointF.y); c.lineTo(this.arrowCenterPointF.x, this.arrowCenterPointF.y); c.moveTo(this.arrowCenterPointF.x, this.arrowCenterPointF.y); c.lineTo(this.arrowEndPointF.x, this.arrowEndPointF.y); c.stroke(); c.closePath();
- 在连续画多种图片/线条的时候,不要连续多次调用draw(true)方法, 消耗性能,一般最后调用fill()/stoke()方法即可。比如
//绘制点 c.beginPath(); let y = node.noteView.pointFrameCenter.y + Constant.FRAME_HEIGHT / 2 + Constant.GAP_BETWEEN_DOT + Constant.RADIUS_DOT; c.setFillStyle(Constant.LINE_COLOR_RED); c.setLineWidth(Constant.LINE_WIDTH); c.arc(node.noteView.pointFrameCenter.x, y, Constant.RADIUS_DOT, 0, 2 * Math.PI); // y += (Constant.GAP_BETWEEN_DOT + Constant.RADIUS_DOT); c.arc(node.noteView.pointFrameCenter.x, y, Constant.RADIUS_DOT, 0, 2 * Math.PI); // // y += (Constant.GAP_BETWEEN_DOT + Constant.RADIUS_DOT); c.arc(node.noteView.pointFrameCenter.x, y, Constant.RADIUS_DOT, 0, 2 * Math.PI); c.fill(); c.closePath();
- 对于draw方法,建议只是调用draw()就好,不要调用draw(true)方法,draw(true)是在原画布之上再去画,不会清空旧画布,draw()会清空。一般,我们会在所有的image,rectangle,line,circle去fill/stoke完之后,再调用draw()方法,这样子就可以避免draw(true)多次,性能耗损。而且再次去reDraw的时候也不用先去清空画布。
- Canvas的跟随手势拖动
//index.wxml <canvas disable-scroll='true' style="width: {{width}}px; height: {{height}}px;background-color:#efeff4;" canvas-id="canvas" bindtouchstart="touchStart" bindtouchmove="touchMove" bindtouchend="touchEnd"></canvas>//index.wcssPage { overflow: hidden; display: flex;}//index.jsconst app = getApp()Page({ data: { width: 0, height: 0, }, onLoad: function (e) { this.time = (new Date()).valueOf(); this.x = 0; this.y = 0; this.moveX = 0; this.moveY = 0; const that = this; wx.getSystemInfo({ success: function (res) { that.setData({ width: res.screenWidth, height: res.screenHeight }) }, }) const ctx = wx.createCanvasContext("canvas", this) this.canvas = ctx; }, onReady: function () { this.draw(); }, draw: function () { this.canvas.fillRect(10, 10, 150, 100) this.canvas.fill(); this.canvas.draw() }, touchMove: function (e) { console.log("touchMove") let xOffset = e.touches[0].x - this.x; let yOffset = e.touches[0].y - this.y; this.x = e.touches[0].x; this.y = e.touches[0].y; this.moveX = this.moveX + xOffset; this.moveY = this.moveY + yOffset; this.canvas.translate(this.moveX, this.moveY); this.draw(); }, touchStart: function (e) { this.x = e.touches[0].x; this.y = e.touches[0].y; }, touchEnd: function (e) { console.log("touchEnd") }})
其他的scale等方法类似。
作者:黎伟杰
链接:微信小程序开发一些经验-教程-小程序社区-微信小程序-微信小程序开发社区-小程序开发论坛-微信小程序联盟
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
原著是一个有趣的人,若有侵权,请通知删除
还没有人抢沙发呢~