icebreaker教你定制小程序代码前言
与普通二维码相比,小程序菊花码辨识度高,微信扫一扫即可。
默认情况下,我们可以自定义生成代码的参数,路径,大小,自动或手动配置线条颜色,背景颜色是否透明。
但是,这些配置项往往无法满足我们的定制需求。
例如,我们需要在不破坏小程序代码可识别性的情况下更换中间的标志。怎么做?接下来笔者就一步步教大家。
整理思路
我们首先要了解问题的本质。这实际上是一个图像处理问题,这个工作可以由服务器端和客户端来完成。
所以有2个选项:
服务器生成代码并拼接结果的服务器端处理方案。具体怎么做,有兴趣的同学,可以查看作者的文章Web函数自定义镜像实战:构建图像处理函数
服务器生成代码,客户端拼接方案。这就是这篇文章特别提到的。
注:小程序代码一般由服务器调用微信api接口生成
云调用——最简单的代码生成解决方案
众所周知,微信小程序环境中的wxacode.getUnlimited有两种生成方式定制小程序,一种是HTTPS调用 外包小程序定制 ,一种是云调用。
其中 小程序开发系统 ,云调用作为针对场景定制的Serverless解决方案定制小程序,往往可以为我们的开发带来效率提升。我们接下来快速部署一个 getWxacodeUnlimit 函数,为我们提供测试资料。
getWxacodeUnlimit/index.js:
import cloud from 'wx-server-sdk'
cloud.init({
env: cloud.DYNAMIC_CURRENT_ENV
})
export async function main (event, context) {
const { scene = '', page, width = 430, autoColor, lineColor, isHyaline } = event
const result = await cloud.openapi.wxacode.getUnlimited({
scene,
page,
width,
autoColor,
lineColor,
isHyaline
})
return result
}
// 笔者使用了打包工具, 大家想直接跑,把 esm 转化为 cjs 即可
getWxacodeUnlimit/config.json:
{
"permissions": {
"openapi": ["wxacode.getUnlimited"]
}
}
有了上面2个代码块,我们的测试功能就部署好了。
将返回的缓冲区转换为本地临时图像
const suffixMap = {
'image/jpeg': 'jpeg',
...
}
export function getPath(filename = 'tmp', contentType = 'image/jpeg') {
return `${wx.env.USER_DATA_PATH}/${filename}.${suffixMap[contentType] || 'jpeg'}`
}
export function writeFile(buff, contentType = 'image/jpeg', filename = 'tmp') {
return new Promise((resolve, reject) => {
const fsm = wx.getFileSystemManager()
const filePath = getPath(filename, contentType)
fsm.writeFile({
filePath,
data: buff,
encoding: 'binary',
success() {
resolve(filePath)
},
fail(error) {
reject(error)
}
})
})
}
....
// 在需要用到的地方直接
try {
loading('生成中')
const result = await getQrcode(scene, option) // 云调用封装function
return await writeFile(result.buffer, result.contentType, scene) // : string
} catch (e) {
console.error(e)
} finally {
loaded()
}
客户端图像处理
说到客户端的图片处理,就不得不提canvas这个原生组件,所以我们只需要用它来衡量一下,把小程序代码中间的Logo部分替换成我们要自定义的图片即可。
测量
这里我们以默认的小程序代码大小 430px * 430px 为例。(为了简单易懂,本案例使用了这个分辨率的小程序代码,如果需要的分辨率不同,可以按比例计算裁剪。)
从图上的注释可以看出,在430px * 430px的分辨率下,上下左右边距为120px,可以计算出中间Logo圆的直径为190px,半径为为 95 像素。
这样你就可以轻松愉快地编写代码了。
用画布 2d 实现
1代小程序的api版本已经淘汰定制小程序,现在直接使用type="2d"版本。Api 文档在 MDN 上。
预标签和样式
<canvas
:class="visible ? '': 'canvas offscreen'"
type="2d"
id="canvas"
:style="{
width:width+'rpx',
height:height+'rpx'
}"
>canvas>
// scss
.canvas.offscreen{ // 2个 class 选择器,增加优先级
position: absolute;
bottom: 0;
left: -9999rpx;
// 这叫物理离屏渲染,笑~
}
注意:您不能在画布上使用 hidden 或 display:none,它们将导致空白渲染。
核心js实现初始化画布实例和上下文
初始化画布实例和 ctx 上下文:
let canvas
let ctx
{...codes...}
onReady(){
uni
.createSelectorQuery()
.in(this) // 如果canvas在组件中,则需要加这一行
.select('#canvas')
.fields({
node: true,
size: true
})
.exec((res) => {
if (res[0]) {
this.canvas = canvas = res[0].node
this.ctx = ctx = canvas.getContext('2d')
// 下面可根据设备的 pixelRatio 自行按比例调整,此处为了演示方便,就直接赋值了。
canvas.width = 430
canvas.height = 430
}
})
}
第一次渲染 - 画布背景
对于第一次渲染,小程序代码作为图像传递到画布中。绘制背景:
async drawBackgroud (orginQrcodeUrl) {
const [err, res] = await uni.getImageInfo({
// 这里可以是远程地址(需要配置downloadUrl)
// 也可以是本地地址(直接返回参数自己)
// 甚至 cloud:// 前缀的云存储url也可以哟
src: orginQrcodeUrl
})
if (err) {
throw err
}
const { path } = res
const img = canvas.createImage()
img.src = path
await new Promise((resolve, reject) => {
img.onload = () => {
// 下面这一行,把小程序码,整个铺进画布中!
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
resolve()
}
img.onerror = (event) => {
reject(event)
}
})
},
第二次渲染 - 裁剪和填充
对于第二次渲染,将背景裁剪为圆形并用图像填充。绘制头像:
async drawAvatar (remoteAvatarUrl) {
const [err, res] = await uni.getImageInfo({
// 比如这里我用了云储存里的图像地址 prefix: cloud://
src: remoteAvatarUrl
})
if (err) {
throw err
}
const { path } = res
const img = canvas.createImage()
img.src = path
// 测量数据在这里用上了
const offsetX = 120 // x 轴偏移 120px
const offsetY = 120 // y 轴偏移 120px
const diam = 190 // 圆的直径 (430 - 120* 2) / 2
const radius = diam / 2 // 圆的半径
const borderWidth = 2 // 多加2px来把原先logo的纯色边抹除
const circle = { // 裁剪部分圆的大小属性
x: offsetX + radius,
y: offsetY + radius,
radius: radius + borderWidth
}
await new Promise((resolve, reject) => {
img.onload = () => {
ctx.save()
// 开始! 把原先中间的Logo干掉!
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI 2, false)
ctx.clip()
// 结束
// 开始! 把我们需要的自定义图像,平铺的插入进去!
ctx.drawImage(
img,
offsetX - borderWidth,
offsetY - borderWidth,
circle.radius * 2,
circle.radius * 2
)
// 结束
ctx.restore()
resolve()
}
img.onerror = (event) => {
reject(event)
}
})
},
通过以上步骤,我们可以轻松完成图片处理部分,将中间的默认Logo替换为自定义图片。
预览并下载到本地
// 获取 tempFilePath
async getImage () {
const [err, res] = await uni.canvasToTempFilePath({
canvas
})
if (err) {
throw err
}
return res.tempFilePath
}
// 预览
async preview (src) {
if (src) {
uni.previewImage({
urls: [src]
})
}
},
// 保存到相册里
async save (src) {
try {
// 先授权,后保存
await authorize('scope.writePhotosAlbum')
const [err, res] = await uni.saveImageToPhotosAlbum({
filePath: src
})
if (err) {
throw err
}
this.$success('保存成功!')
} catch (e) {
console.error(e)
}
}
至此,客户端生成自定义小程序代码的完整解决方案就完成了
显示结果
或者在微信搜索程序员名片后,维护名片,上传头像,然后点击下方分享二维码按钮进行预览。
自定义生成转发图片
这篇文章的案例很简单。笔者还写了一个小程序Canvas 2D自定义生成和转发图片,以及自定义分享海报。这些原理在原理上是相似的。
附录
wxacode.getUnlimited 接口文档
本站声明: 本文章内容来源于互联网,文章内容仅供用户参考。本公司不能完全保证文章内容的准备性、时效性。如果因本文章对用户造成了任何损失或者损害,本公司将不会承担任何法律责任。如果涉及到版权问题,请提交到wikins@nbyuyuan.com