在 2021 年的国庆前两天,群里有人说,想弄一个给微信头像增加国旗渐变和国庆节气氛边框的小程序,并发出了一个案例。
我打开看了看,这感觉挺简单的。然后脑海中大概的形成了一个实现思路,然后说干就干,花了两个晚上的时间,就把代码给撸了出来,赶在放国庆假期之前完成了上线。
先体验一下吧,微信搜索小程序 Falost
下面就来说说实现思路吧~
这个小程序没有什么太多花里胡哨的东西,实现思路很简单
1、首先利用 canvas 将需要添加边框的头像给绘制出来;
2、然后将边框头像进行重叠绘制,保存绘制完成后的图片进行保存即可。
这思路是不是超级简单,对于一个前端开发来说,完全没有压力呀。
正所谓万事开头难,我得先确定技术方案,虽然功能不难,但是技术方案还需要选择一下。原生、taro、uniapp,就从这三个开发方案中选择了,因为考虑到使用后台管理边框数据,最终选择了 uniapp 云开发,至于你问我为什么选择 uniapp 云开发而不是原生云开发,因为 uniapp 免费啊!
好了,技术方案选择完成,那么接下来就把我之前开发的,一个uniapp云开发的小程序框架拿了出来进行了一番修改,很完美的就运行了起来。
但是,另外一个问题来了,没有素材怎么办,只能去网上找,结果很不错,在开源社区找了不少,让这个小程序开发起来更简单了。
接下来就是码代码的过程了。
设计数据表,说到设计数据库,uniapp 是真的很简单。先在项目根目录创建 uniCloud-aliyun/database 目录用来存放 schema 数据结构。schema 的具体使用可以看看官方文档。
// 文档教程: https://uniapp.dcloud.net.cn/uniCloud/schema
{
"bsonType": "object",
"required": ["image"],
"permission": {
"read": true,
"create": false,
"update": false,
"delete": false
},
"properties": {
"_id": {
"description": "ID,系统自动生成"
},
"title": {
"title": "边框名称",
"bsonType": "string",
"description": "边框名称",
"trim": "both"
},
"origin": {
"bsonType": "file",
"title": "图片",
"description": "边框图片",
"fileMediaType": "image",
"fileExtName": "jpg,png",
"componentForShow": {
"name": "image"
}
},
"type": {
"bsonType": "int",
"title": "分类",
"description": "分类",
"foreignKey": "wz_border_image_category.type",
"enum": {
"collection": "wz_border_image_category",
"field": "name as text, type as value",
"where": "status == 1 && type > 0",
"orderby": "sort asc"
}
},
"hot": {
"title": "热门推荐",
"bsonType": "int",
"description": "是否热门推荐",
"enum": [{
"value": 1,
"text": "推荐"
},
{
"value": 0,
"text": "不推荐"
}
]
},
"sort": {
"bsonType": "int",
"description": "值越大越靠后",
"title": "排序"
},
"status": {
"title": "状态",
"bsonType": "int",
"description": "状态",
"enum": [{
"value": 1,
"text": "启用"
},
{
"value": 0,
"text": "不启用"
},
{
"value": -1,
"text": "删除"
}
]
},
"create_at": {
"title": "创建时间",
"bsonType": "timestamp",
"description": "创建时间",
"componentForShow": {
"name": "uni-dateformat"
},
"permission": {
"read": true,
"write": false
},
"forceDefaultValue": {
"env": "now"
}
},
"update_at": {
"title": "更新时间",
"bsonType": "timestamp",
"description": "更新时间",
"permission": {
"read": true,
"write": false
},
"componentForShow": {
"name": "uni-dateformat"
},
"forceDefaultValue": {
"env": "now"
}
}
}
}
数据表创建完成之后,就是将获取到边框素材进行一个整理,然后通过脚本将素材文件上传到 uniapp 的云储存中,并保存数据记录。
let result = await uniCloud.uploadFile({
cloudPath: item.image.replace('../', ''),
fileContent: path.join('../data/json/', item.image)
});
const data = {
type: item.type || 0,
image: result.fileID,
title: item.title ||'',
sort: item.sort || 0,
status: 1
}
const res = await db.collection('wz_border_image_urls').add(data)
// item 是整理的素材文件 json
然后,就可以开始前端代码的完成了。
先构建基本页面的框架,然后让我们的小程序,看起来好看一点吧(设计感一般般,将就看吧)
前端页面代码就不贴了,没啥亮点。直接看 js 内容吧
1、获取云端边框数据,直接调用云函数即可。
async getBorderImage(on = 1) {
if (this.data.borderList[this.data.borderType]) return
// 点击切换菜单 回复初始状态
const type = this.data.borderType || 0
if (on === 1) {
// this.data.borderList[this.data.borderType] = []
this.data.pageIndex = 0
}
this.data.pageIndex = this.data.pageIndex + 1
const filter = {
status: 1
}
if (type === 0) {
filter.hot = 1
} else {
filter.type = type
}
const {
result
} = await uniCloud.callFunction({
name: 'query_list',
data: {
dbName: "wz_border_image_urls",
filter,
order: {
"name": "sort",
"type": "asc"
},
pageIndex: this.data.pageIndex,
pageSize: 100
},
})
if (result.code === 0) {
if (result.data.hasMore) {
this.data.loadStatus = 'loadmore'
} else {
this.data.loadStatus = 'nomore'
} this.data.hasMore = result.data.hasMore
let list = [...(this.data.borderList[this.data.borderType] || []), ...(result.data.list || [])]
this.set(this.data.borderList, `{this.data.borderType}`, list)
if (this.data.selectBorder) {
let item = list.find(item => String(item._id) === String(this.data.selectBorder))
this.changeBorder(item)
}
}
}
2、获取云端边框分类数据,同上调用
async getBorderCategory() {
const {
result
} = await uniCloud.callFunction({
name: 'query_list',
data: {
dbName: "wz_border_image_category",
filter: {
status: 1,
},
order: {
"name": "sort",
"type": "asc"
},
pageIndex: 1,
pageSize: 50
},
})
if (result.code === 0) {
this.$data.borderCategory = result.data.list
}
}
3、绘制边框,首先初始化 canvas 画布,我们需要将边框素材图片和头像下载到本地,才能进行后面的 canvas 绘制
initCanvas() {
this.data.ctx = uni.createCanvasContext(this.data.canvasId)
this.data.ctx.setFillStyle(this.data.canvas.background)
this.data.ctx.fillRect(0, 0, this.data.canvas.width, this.$data.canvas.height)
// 画用户头像
this.drawAvatarUrlImage()
}
4、绘制用户用户头像,需要先将微信头像下载下来
drawAvatarUrlImage() {
let avatarSrc = this.data.avatarSrc || ''
uni.downloadFile({
url: avatarSrc,
success: (res) => {
console.log('result', res)
this.data.ctx.drawImage(res.tempFilePath, this.changeSize(0), this.changeSize(0), this.data.screenWidth, this.data.screenWidth)
// 画头像框
this.drawAvatarBorderImage()
},
fail: function() {
uni.hideLoading()
uni.showModal({
title: '提示',
content: '无法下载头像',
})
}
})
}
5、然后绘制头像边框,同意需要下载到本地
drawAvatarBorderImage() {
let borderImg = this.data.borderImage
// 没有头像框
if (!borderImg) {
// 开始生成
return this.draw()
}
// // 开始生成
// return this.draw()
// 有头像框则继续执行
uni.downloadFile({
url: borderImg,
success: (result) => {
this.data.ctx.drawImage(result.tempFilePath, this.changeSize(0), this.changeSize(0), this.data.screenWidth, this.data.screenWidth)
// 开始生成
this.draw()
},
fail: function() {
uni.hideLoading()
uni.showModal({
title: '提示',
content: '无法下载头像框',
})
}
})
}
6、最后一步就是绘制头像,并保存到相册就好
draw() {
this.data.ctx.draw(false, () => {
setTimeout(() => {
this.canvasToImage()
}, 130)
})
}
// 生成图片
canvasToImage() {
uni.canvasToTempFilePath({
canvasId: this.data.canvasId,
success: (res) => {
uni.hideLoading()
this.data.ctx = null
console.log(res)
this.toast('生成成功,正在保存', 'center', 'success', 2000)
this.data.imageBill = res.tempFilePath
uni.setStorageSync('imageBill', this.data.imageBill)
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success: () => {
// this.tips('保存成功\n不妨分享一下~', '', 3500)
wx.showToast({
title: '保存成功\n不妨分享一下~',
icon: 'none',
duration: 3000
})
uni.navigateTo({
url: `/pages/avatar/result?url={this.data.imageBill}`
})
// // 生成以后直接预览图片
// uni.previewImage({
// current: res.tempFilePath,
// urls: [ res.tempFilePath ],
// })
},
fail: () => {
this.toast('保存失败\n请授权相册权限~', 'error', 3000)
}
});
},
fail() {
uni.hideLoading()
uni.showModal({
title: '提示',
content: '头像保存失败',
})
}
})
}
到这里,我们的小程序想要功能基本已经完成了,但是我们为了更友好的使用,我加上了本地图片上传还有拍照头像进行编辑的功能。这里我们就需要一个裁剪功能,将拍照图片和本地上传的图片,进行一个裁剪,以便我们头像的使用,这里我们之间使用了一个开源组件 image-cropper 很方便使用,当然这里为了更好的使用,我做了一个定制化,具体可以看代码。
为了配合微信运营规范,小程序上传的图片进行了内容安全的检测,禁止色情、暴力和政治相关的内容等。
国庆上线版本就完成了,但是后来,为了有更多的花样,我添加了头像自定义功能,可以多个元素一起编辑和单个边框进行编辑。提供了更多的可玩性,并且,加入了流量主,获取一点小小的收益也是很不错的哦。
为了回馈开源社区,我在半个月前,将代码进行了开源。方便大家一起学习交流。
今天,我上线了一个红包封面领取的功能,群里有朋友想要代码。然后,我就一起放了上来,如果有需要的话,自己拿便可。
如果有兴趣,可以添加我微信(falost_cc)备注小程序,拉你进小程序交流群一起交流哦~
源码地址在公众号后台,关注 Falost 并回复 “头像边框源码” 即可获取。
转载请注明:Falost的小窝 » uniapp云开发头像边框小程序和红包封面小程序