uniapp云开发头像边框小程序和红包封面小程序

897 阅读5分钟

在 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 并回复 "头像边框源码" 即可获取。

如果有兴趣,可以添加我微信(falost_cc)备注小程序,拉你进小程序交流群一起交流哦~