# Uni-app微信小程序
# 本文为简素言也原创,若有错误敬请指正
# 一、微信授权登录全流程
1.定位授权
2.个人信息授权
3.获取唯一 openID
# 二、微信支付全流程
微信公众号服务号、公众号商城、包括小程序,都是用的JSAPI支付方式
# 1.开发前准备
# (1)在 微信商户平台 (opens new window)开通微信商户(该步骤较为麻烦)
获取商户号MCH_ID和设置商户API密钥,申请API证书
如果需要退款,还需要设置APIv3密钥
# (2)微信公众平台,小程序提交认证
需填写各种信息,认证务必选择企业类型,提供企业经营证书及社会信用码等等,需要300元费用,认证很快
# (3)开通小程序支付功能
将小程序的appid绑定到第一步开通的商户号上
# 2.代码实现
# (1)JSAPI统一下单
将下单的商品信息和openId
发给后端,
后端 通过统一下单接口,进行第一次签名,需要的参数如下:
//APPID:小程序的 AppID
//MCH_ID:商户号
//KEY:商户支付API密钥
//APPSECRET:小程序开发者密钥
如果在统一下单的时候,不填写sign_type为MD5,则会默认使用HMAC-SHA256加密,这个是我们要排的第一个坑。
微信后台接到统一下单参数后,会生成一个商户订单,并将预下单id(prepay_id )返回给后端
# (2)二次签名
后端 根据微信返回值,通过微信提供的算法,进行二次签名
将生成的二次签名和其他参数返回给前端
# (3)前端通过uni.request调起微信支付接口,代码如下
// 唤起微信支付
uni.requestPayment({
provider: "wxpay",
appId: res.result.appId, //小程序appid
timeStamp: res.result.timeStamp, //生产的时间戳
nonceStr: res.result.nonceStr, //生成的随机字符
package: res.result.package, //prepay_id
signType: res.result.signType, //加密方式,与统一下单时的一致
paySign: res.result.sign, //二次签名,由后端基上面五个参数算出来的
success: (res) => {
console.log("支付成功", res);
console.log("success:" + JSON.stringify(res));
uni.showToast({
title: "支付成功",
duration: 3000,
icon: "none",
});
setTimeout(function () {
uni.switchTab({
url: "xxx",
});
}, 1500);
},
fail: (err) => {
uni.showModal({
content: "支付失败,原因为:\n " + err.errMsg,
showCancel: false,
});
console.log("fail:" + JSON.stringify(err));
},
});
# (4)遇到的报错坑
调用支付JSAPI缺少参数: total_fee ==>
说明一定是第三步里提交的参数有问题,特别注意package参数,要以 “prepay_id=xxxxxxxxxx” 的形式发送
或者是参数的大小写问题
支付验证签名失败 ==>
这是签名的问题,通常是第二次生成的签名不对,可以用官方的校验工具检查一下签名校验在线工具 (opens new window)
二次签名方式一定要与统一下单接口使用的一致,比如均为MD5
注意二次签名的变量名的大小写为小驼峰型,与下面的文档保持一致,否则生成的paySign不对
# (5)其他未尝试的办法
uniapp提供了一种封装好的unipay插件模板,只需要将小程序的 AppID,商户号,商户密钥等等参数传入,
通过云函数,unipay.initWeixin会帮助你生成最终提供给前端的参数
同理,小程序云开发也提供了同种功能,但是因为云开发需要付费,未曾尝试过。。。
# 三、动态计算显示高度
# 1.获取手机型号
uni.getsystemInfo(){}
# 2.获取节点高度
//获取节点信息
uni.createSelectorQuery()
.in(this)
.select("#one")
//根据节点高度与页面滚动距离scrollTop得到所需滑动的距离
.boundingClientRect(data => {
uni.pageScrollTo({
duration:0,
scrollTop: that.scrollTop + data.top-44
});
}).exec(
# 四、scroll吸附贴顶效果
# 方法1. 监听onPageScroll
事件,滚动到指定位置添加fixed样式
在Vue中,监听滚动事件,打印当前的scrollTop
首先,在mounted钩子中给window添加一个滚动滚动监听事件,
mounted () {
window.addEventListener('scroll', this.handleScroll)
},
然后在方法中,添加这个handleScroll
方法
监听元素到顶部的距离 并判断滚动的距离如果大于了元素到顶部的距离时,设置searchBar
为true,否则就是false
handleScroll () {
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
var offsetTop = document.querySelector('#searchBar').offsetTop
if (scrollTop > offsetTop) {
this.searchBarFixed = true
} else {
this.searchBarFixed = false
}
},
先写一个该元素固定到顶部的样式,isFixed
然后将需要固定的元素的class与searchBar
进行绑定,如果searchBar
为true时,就应用这个isFixed
样式
<view class="searchBar" id="searchBar">
注意,如果离开该页面需要移除这个监听的事件,不然会报错。
destroyed () {
window.removeEventListener('scroll', this.handleScroll)
},
存在问题:获取指定位置错误,因为上面都是图片,在图片未加载出来时获取高度, 高度值不对,解决办法就是在imgae
上加bindload
事件,在图片加载加载完成之后再获取高度; onPageScroll
事件会有延迟,导致最终效果会出现卡顿
# 方法2. 通过position:sticky
该元素并不脱离文档流,仍然保留元素原来在文档流中的位置;
当元素在容器中被滚动超过指定的偏移值时,元素就会固定到容器的指定位置,也就是说如果元素设置设置top:50px
那么在sticky元素滚动到距离相对定位元素的顶部50px
时固定,不再向上移动;
元素固定的相对偏移是相对于离他最近的具有滚动框的祖先元素,如果祖先元素都没有滚动框,那么就是相对于viewport
来计算元素的偏移量
tip:需要考虑父元素的高度的情况,sticky元素在到达父元素的底部时,则不会再发生定位,如果父元素并没有比sticky元素高,那么sticky元素一开始就到达了底部,就不会有定位的效果,当元素滚动到父元素的底部时sticky属性失效,如果父元素的overflow属性不是默认的visible,那么sticky属性不会生效
兼容性不是很好,只支持FireFox
和Safari
,移动端
# 五、小程序生成海报图片并保存至本地
这里的痛点在于,uniapp和小程序不能直接操作DOM元素,所以一开始想使用 html2canvas ,不能实现
尝试其他方法:
# 方法1. 使用原生canvas API直接绘制图片(最原始的方法)
但即使最原始的方法,依旧存在一些踩坑,譬如微信小程序官方在2.9.0开始支持了一个canvas 2D的新API,之前的API不再进行维护,因此之后使用canvas的项目,都建议使用canvas 2D来绘制
canvas 常用api和 canvas 2D用法 跳转这里
那么canvas2D 有哪些变化呢
全面支持源生H5 JS的写法,迁移H5代码更容易,学习成本更低
性能上的优化和提升,复杂动画上帧数明显
BUG修复,以及一些不支持的条件完善
# 方法2. 使用插件
# 1.painter
painter git地址:https://github.com/Kujiale-Mobile/Painter 可以在这个演示地址 https://lingxiaoyi.github.io/painter-custom-poster 先布局好,然后复制代码
具体使用步骤: 1.下载painter 将components文件下的 painter 文件复制到自己的项目里,然后再使用的页面进行引入 注意:第三方框架编写的小程序需要放到 wxcomponents或者static文件下,默认会报错 不能放在
"usingComponents": {
"painter":"/wxcomponents/painter/painter"
}
如果使用时遇到这个问题,regeneratorRuntime is not defined,解决方案:勾选增强编译 即可
2.在页面中使用
<painter customStyle='width:630rpx; height: 732rpx;' :palette="canvasdata" :dancePalette="canvasdata" @imgOK="onImgOK"/>
data(){
return{
canvasdata:'',
canvasImgUrl:''
}
}
设置动态数据
onSelect() { //设置数据的方法,内容较少仅举例方便理解
var _this = this
this.canvasdata = {
width:"630rpx",
height:"732rpx",
background:'#fff',
views:[
{
type: 'image',
url: _this.DetailData.avatar,
css: {
width: '78rpx',
height: '78rpx',
borderRadius: '39rpx',
top: '30rpx',
left: '30rpx',
}
},
{
"type": "text",
"text": _this.DetailData.nickname,
"css": {
"color": "#434343",
"width": "200rpx",
"top": "28rpx",
"left": "128rpx",
"fontSize": "28rpx"
}
}
]
}
},
3.保存图片
onImgOK(e){
// console.log(e)
this.canvasImgUrl = e.detail.path
}
savePoster(){ //保存海报
var _this = this;
uni.saveImageToPhotosAlbum({
filePath: _this.canvasImgUrl,
success(result) {
uni.showToast({
title: '图片保存成功',
icon: 'none'
})
}
})
}
# 2.小程序原生内置扩展组件, wxml-to-canvas (opens new window)
正常原生小程序开发,如果要增加扩展组件wxml-to-canvas
只需要 npm install --save wxml-to-canvas,后面再增加JSON组件声明,wxml引入组件即可
执行之后,会在项目根目录下创建node_modules目录,但是这个node_modules 目录不会参与小程序编译、上传和打包,所以要通过开发者工具“工具-构建 npm”,这样就会在node_modules 的同级目录下会生成一个 miniprogram_npm 目录,里面会存放构建打包后的 npm 包,也就是小程序真正使用的 npm 包。
但是uniapp开发的小程序不一样,首先uniapp项目里会有自己的package.json文件,安装npm install之后,会在项目根目录生成一个node_modules目录,里面是所有第三方的安装包,包括uniapp的所有包,核心、编译、解析等等。
经过uniapp打包之后,生成的原生小程序项目(也就是我们最终给开发者工具使用的项目包),里面是不包含node_modules目录,也就没办法通过开发者工具“工具-构建 npm”生成miniprogram_npm 目录
# 解决方案是
下载官方wxml-to-canvas的代码片段
我们在目录里找到miniprogram_npm目录,里面包含三个已经打包好的文件,分别是eventemitter3、widget-ui、wxml-to-canvas
我们将其中两个个文件widget-ui、wxml-to-canvas拷贝下来,放到我们的uniapp项目里
widget-ui文件放到wxcomponents/widget-ui/miniprogram_npm/widget-ui
wxml-to-canvas文件放到wxcomponents/wxml-to-canvas/miniprogram_npm/wxml-to-canvas
然后在全局引入wxml-to-canvas
"usingComponents": {
"wxml-to-canvas": "/wxcomponents/wxml-to-canvas/miniprogram_npm/wxml-to-canvas/index",
}
另外需要修改wxml-to-canvas/index.js
module.exports = require("../../../widget-ui/miniprogram_npm/widget-ui/index")