本文将对微信小程序从开始开发到发布上线的详细过程进行讲解,欢迎大家加入微信群交流讨论,点击获取入群二维码
《超级颜值计算器》小程序源码托管在Github上,可自行clone修改: https://github.com/dreamans/B...
开发前的准备工作
开发前准备工作请参考以下文章:
微信小程序开发: 开发前准备工作
微信小程序开发: 小程序源文件结构及含义
小程序预览
小程序名称:《超级颜值计算器》
小程序码:
UI预览:
账号申请
注册小程序
注册小程序, 注册成功后进入邮箱查找激活邮件,如实填写相关信息,如下:
设置小程序信息
注册成功后会进入小程序发布流程
,按要求设置小程序相关信息即可,如下:
AppID&&AppSecret
在 设置
- 开发设置
中获取小程序的AppID和AppSecret
其他接口申请
小程序依赖face++的图像面部识别接口,小伙伴可到其官网上自行申请账号,申请地址,接口文档地址
注册成功后登录face++的Console平台,进入应用管理
- API Key
获取接口所需的key和secret。
有一点问题需要注意,免费版用户被限制了调用并发量,会经常出现 CONCURRENCY_LIMIT_EXCEEDED 并发数超过限制
错误。
开始开发
开发者工具
运行 开发者工具
, 选择 小程序项目
,填写项目目录、AppID(mp平台中获取)、项目名称等。
小程序配置文件
在根目录下创建小程序配置文 app.json
,并配置相关信息
小程序页面列表
每一项代表一个页面,第一项代表程序的初始页面,此处我们设置了两个页面,分别是:
pages/index/index
程序主页面pages/reward/reward
程序打赏页面
"pages": [
"pages/index/index",
"pages/reward/reward"
],
window 设置
接下来我们来设置小程序的导航栏样式和标题名称等window属性:
"window": {
"navigationBarTitleText": "超级颜值计算器",
"navigationBarBackgroundColor": "#ff8c00",
"navigationBarTextStyle": "white",
"navigationStyle": "default",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light",
"enablePullDownRefresh": false
},
tabBar 设置
此次我们开发的小程序是一个多tab的应用,以下是tabBar的设置信息:
"tabBar": {
"color": "#9ca0a3",
"selectedColor": "#00ae66",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "asset/icon/home.png",
"selectedIconPath": "asset/icon/home_selected.png"
},
{
"pagePath": "pages/reward/reward",
"text": "打赏",
"iconPath": "asset/icon/reward.png",
"selectedIconPath": "asset/icon/reward_selected.png"
}
]
},
其他配置信息请见源文件
全局样式设置
在根目录下创建全局样式文件 app.wxss
,会作用于当前小程序的所有页面。
当前小程序所使用的全局样式:
.icon-arrow:after{
content: "\2715";
}
.icon-love:after {
content: "\2740";
}
.icon-male:after {
content: "\2642";
}
.icon-female:after {
content: "\2640";
}
全局注册小程序函数
小程序启动之后,在 app.js 中执行App函数用来注册一个小程序,onLaunch
回调会在小程序初始化完成时触发,并且全局只触发一次。
在根目录下创建 app.js
App({
onLaunch() {
let self = this;
wx.getSystemInfo({
success: function (res) {
self.globalData.screenWidth = res.windowWidth;
self.globalData.screenHeight = res.windowHeight;
}
});
},
globalData: {
screenWidth: null,
screenHeight: null
}
});
代码说明:
小程序在初始化完成后会调用微信获取系统信息API(wx.getSystemInfo
) ,获取窗口的宽高,并存放到globalData
属性中,供其他page使用。
主页面开发
文件列表:
pages/index/index.js 核心js业务逻辑
pages/index/index.wxml 模板文件
pages/index/index.wxss 样式文件pages/index/index.json (暂未用到)
创建视图
创建 pages/index/index.wxml
文件,代码如下:
<view class="container">
<view class="pic-info" style="width: {{ picSelfAdaptWidth }}vw; height: {{ picSelfAdaptHeight }}vh">
<image wx:if="{{ !testPicFile }}" class="pic-none" src="../../asset/icon/pic_bg.png"></image>
<image wx:else class="pic-inner" src="{{ testPicFile }}"></image>
</view>
<view wx:if="{{ testPicFile && !testPicResult }}" class="close" bindtap="handleCancelPic">
<i class="arrow">×</i>
</view>
<view class="pic-result" wx:if="{{ testPicResult }}">
<view class="score-box"><i class="icon-love"></i> 颜值 <span class="score">{{ testPicResult.beauty }}分</span>, 击败 {{ testPicResult.defeat}}% 用户</view>
<view class="score-ext">
<span wx:if="{{ userInfo }}">用户: {{userInfo.nickName}}</span>
<span>性别: <i class="icon-{{ testPicResult.gender }}"></i></span>
<span>年龄: {{ testPicResult.age }}</span>
</view>
</view>
<view>
<button wx:if="{{ !testPicFile }}" class="btn btn-select" bindtap="handleUploadPic">选择照片/拍照</button>
<button wx:if="{{ testPicFile && !testPicResult }}" class="btn btn-compute" open-type="getUserInfo" bindgetuserinfo="handleGetUserInfo">计算颜值</button>
<button open-type="share" wx:if="{{ testPicResult }}" class="btn btn-share">邀请好友来玩</button>
<button wx:if="{{ testPicResult }}" class="btn btn-bottom" bindtap="handlePlayAgain">再试一次</button>
</view>
</view>
变量说明
picSelfAdaptWidth
为图片容器显示宽度,默认为 70vw
;
picSelfAdaptHeight
为图片容器显示高度,根据图片真实宽高和默认宽度计算得来,具体计算方法见 源代码;
testPicFile
为用户选取的照片临时地址;
testPicResult
为照片识别结果集对象,所包含的属性有 beauty:颜值(0-100)
, defeat: 击败用户百分比
, gender: 性别
, age: 年龄
;
userInfo
为用户基本信息,包括 nickName: 用户昵称
;
事件说明
handleUploadPic
点击选取照片时触发的事件;
handleGetUserInfo
点击计算颜值时触发的事件;
handlePlayAgain
点击再试一次时触发的事件;
handleCancelPic
选取完照片后点击右上角红叉时触发的事件;
创建样式
创建 pages/index/index.wxss
文件,代码如下:
page {
background-color: #ff8c00;
}
.container {
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
}
.pic-info {
margin-top: 6vh;
border: solid #fff;
border-width: 30rpx 30rpx 30rpx 30rpx;
box-shadow: 10rpx 10rpx 15rpx #333;
background: #fff;
display: flex;
align-items: center;
justify-content:center;
border-radius: 20rpx;
}
.btn {
background: #dd5900;
color: #fff;
border: 1px solid #fff;
}
.btn-select {
margin-top: 80rpx;
}
.btn-compute {
margin-top: 50rpx;
margin-bottom: 50rpx
}
.btn-share {
margin-top: 50rpx;
}
.btn-bottom {
margin-top: 30rpx;
margin-bottom: 50rpx;
}
.pic-none {
width: 90%;
height: 90%;
}
.pic-inner {
width: 100%;
height: 100%;
}
.pic-result {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 50rpx;
color: #fff;
}
.pic-result .score-box{
font-size: 34rpx;
}
.pic-result .score {
font-size: 50rpx;
}
.pic-result .score-ext {
margin-top: 30rpx;
font-size: 28rpx;
}
.pic-result .score-ext span {
margin-right: 30rpx;
}
.pic-result .score-box {
background: #fff;
padding: 10rpx 20rpx;
border-radius: 20rpx;
color: #dd5900;
}
.close {
position: absolute;
top: 7vh;
right: 12vw;
}
.close .arrow {
display: flex;
align-items: center;
justify-content:center;
font-size: 38rpx;
width: 50rpx;
height: 50rpx;
border-radius: 25rpx;
background: #dd5900;
color: #fff;
}
创建视图JS逻辑文件
创建 pages/index/index.js
文件,代码如下:
const app = getApp()
const picDefaultWidth = 50;
const picDefaultHeight = 30;
const picTestFileDefaultWidth = 70;
Page({
onShareAppMessage(res) {
return {
title: '我的颜值击败了全国 94% 的用户,不服来战!',
path: '/pages/index/index',
imageUrl: '../../asset/img/prview.png'
}
},
data: {
picSelfAdaptWidth: picDefaultWidth,
picSelfAdaptHeight: picDefaultHeight,
testPicFile: '',
testPicResult: null,
userInfo: null
},
handleGetUserInfo(e) {
this.setData({
userInfo: e.detail.userInfo
});
this.handleComputePic();
},
handleCancelPic() {
this.setData({
testPicFile: '',
testPicResult: null,
picSelfAdaptHeight: picDefaultHeight,
picSelfAdaptWidth: picDefaultWidth
});
},
handleUploadPic() {
let self = this;
let ret = wx.chooseImage({
count: 1,
sizeType: "compressed",
success: function(res) {
self.setData({
testPicFile: res.tempFiles[0].path
});
self.getImageInfo(res.tempFiles[0].path, function(res) {
self.setPicAdaptHeight(res.width, res.height);
});
}
});
},
handlePlayAgain() {
this.setData({
testPicFile: '',
testPicResult: null,
picSelfAdaptHeight: picDefaultHeight,
picSelfAdaptWidth: picDefaultWidth
});
},
handleComputePic() {
let self = this;
wx.showLoading({
title: "颜值计算中",
mask: true
});
wx.uploadFile({
url: 'https://api-cn.faceplusplus.com/facepp/v3/detect', //仅为示例,非真实的接口地址
filePath: self.data.testPicFile,
name: 'image_file',
formData: {
'api_key': 'DVc8JblEbcBjgq55TtDW0sheUhBeCaGe',
'api_secret': 'lMUVhSAg_ruN4PmwgNCk0IiWPNAF2_Sr',
'return_attributes': 'gender,age,beauty'
},
success: function (res) {
if (res.statusCode != 200) {
wx.showToast({
title: '服务器被挤爆了,请稍后再试',
icon: 'none',
duration: 2000
});
return false;
}
let data = JSON.parse(res.data);
console.log(data)
console.log(res)
if (!data.faces || data.faces.length == 0) {
wx.showToast({
title: '没有识别到人脸,请更换照片重试',
icon: 'none',
duration: 2000
});
return false;
}
let human = [];
data.faces.forEach(item => {
let beauty = 50;
if (item.attributes.gender.value == 'Male') {
beauty = item.attributes.beauty.female_score;
} else {
beauty = item.attributes.beauty.male_score;
}
human.push(beauty);
});
let beautyIndex = human.indexOf(Math.max.apply(null, human));
let maxBeautyHuman = data.faces[beautyIndex];
let humanAttr = {
age: maxBeautyHuman.attributes.age.value,
gender: maxBeautyHuman.attributes.gender.value,
beauty: 50
};
if (humanAttr.gender == 'Male') {
humanAttr.beauty = maxBeautyHuman.attributes.beauty.female_score;
} else {
humanAttr.beauty = maxBeautyHuman.attributes.beauty.male_score;
}
humanAttr.gender = humanAttr.gender == 'Male' ? "male" : "female";
humanAttr.beauty = Math.ceil(humanAttr.beauty) + 15;
humanAttr.beauty = humanAttr.beauty > 97 ? 97 : humanAttr.beauty;
humanAttr.defeat = self.computeBeautyDefeatRatio(humanAttr.beauty);
self.setData({
testPicResult: humanAttr
});
wx.hideLoading();
},
fail: function() {
wx.showToast({
title: '网络异常,请稍后再试',
icon: 'none',
duration: 2000
});
}
})
},
getImageInfo(imgSrc, scb, ecb) {
wx.getImageInfo({
src: imgSrc,
success: scb,
fail: ecb
});
},
setPicAdaptHeight(picWidth, picHeight) {
let h = (app.globalData.screenWidth * 0.7 / picWidth) * picHeight / app.globalData.screenHeight * 100;
this.setData({
picSelfAdaptHeight: h,
picSelfAdaptWidth: picTestFileDefaultWidth
});
},
computeBeautyDefeatRatio(beauty) {
return Math.ceil(Math.sqrt(beauty) * 10);
}
})
执行流程
启动小程序
- App()函数执行并且
onLaunch
回调被触发,获取window的宽高,存入globalData
属性中;
首页渲染
- 为
picSelfAdaptWidth
和picSelfAdaptHeight
分别赋默认值,显示照片显示区显示默认图片; - 此时
testPicFile
,testPicResult
均为空,视图将只显示 "选择照片/拍照" 按钮;
点击"选择照片/拍照"按钮
- 触发
handleUploadPic
事件, 通过调用wx.chooseImage
来选择本地图片或者拍照,然后将选取的照片临时路径赋值给testPicFile
; - 然后调用自定义方法
getImageInfo
来获取图片的宽高等信息,再调用setPicAdaptHeight
传入宽高来计算图片自适应高的值picSelfAdaptHeight
; - 此时由于
testPicFile
非空,"选择照片/拍照"按钮将隐藏,"计算颜值"按钮显示; - 同时图片预览区右上角出现"红叉"按钮;
点击"红叉"按钮
- 将会触发
handleCancelPic
事件,会重置data对象中所有属性值,回到最初始状态;
点击"计算颜值"按钮
- 首先使用open-type(微信开放能力)开放的功能获取用户信息(从bindgetuserinfo绑定的回调函数中获取用户信息),此时会触发
handleGetUserInfo
, 获取用户信息后会执行this.handleComputePic
颜值计算方法,
颜值计算方法核心逻辑
- 调起Loading,显示
颜值计算中
; - 通过微信API
wx.uploadFile
向face++接口发送请求,查看源代码; -
对face++API的返回值进行进一步处理:
- 颜值数据中会有同性颜值打分和异性颜值打分,我们取异性颜值打分数据;
- 返回数据中若有多组面部打分数据的话,我们取颜值最高一组;
- 为原始颜值分
+15
分的 buff, 最高不超过97分; - 计算击败率,具体算法请见源码 ;
邀请好友
- 使用
button
组件的open-type
功能来触发用户转发; - 在 Page 中定义
onShareAppMessage
函数,设置该页面的转发信息 参见文档
Page({
onShareAppMessage(res) {
return {
title: '我的颜值击败了全国 94% 的用户,不服来战!',
path: '/pages/index/index',
imageUrl: '../../asset/img/prview.png'
}
},
...
点击"再试一次"按钮
- 触发
handlePlayAgain
事件,重置data中全部属性值;
handlePlayAgain() {
this.setData({
testPicFile: '',
testPicResult: null,
picSelfAdaptHeight: picDefaultHeight,
picSelfAdaptWidth: picDefaultWidth
});
}
打赏页面开发
打赏页面比较简单,不进行讲解,可自行参考源码
测试
在开发者工具中有 测试
按钮,可点击申请测试报告,如下:
报告每24小时可申请一次,内容如下:
发布
上传代码
首先需要上传小程序代码,点击 上传
按钮进行上传
填写版本号,项目备注后就开始上传代码
提交审核
登录微信公众平台,进入 开发管理
找到开发版本卡片,点击 提交审核
如实填写审核信息
等待审核通过后会有微信通知,大概1个工作日我就收到审核通过的通知了(赞一下微信的审核速度),然后登陆公众平台,进入 开发管理
,你会看小程序的状态是审核通过待发布,点击 发布
即可。
可以打开微信在搜索框中输入小程序名称,如果能正常搜索到,说明此次发布成功。
结尾
至此,小程序就顺利诞生了,希望此文章对刚入坑小程序开发的伙伴们有所帮助。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。