前言
在分享小程序时,如果需要加上很多额外广告信息,那么按钮分享则无法满足要求,于是出现了项目(广告)海报这个解决方案,在做好的海报图中加上微信小程序码让用户下载后分享图片给好友或者直接发送朋友圈,好友点击图片时可以识别小程序码直接导航进入小程序。
生成海报图方案
wxml-to-canvas
微信官方提供的一个插件,可以将小程序内的静态模板转换为图片。插件官网
- 缺点:uniapp中导入插件麻烦,还需各种配置。使用起来十分繁琐。
- 优点:个人觉得无,但是有的使用者觉得相比较其他方案(canvas)无需额外学习api。但本身这个插件其实就是配置式的一种canvas。
const style = {
class1: { // class名
height: 30,
widht: 100
},
class2: {} // 不区分dom的嵌套关系只会匹配类名
}
const dom = `
<view class="class1">
<view class="class2"></view>
</view>
`
// 使用页面
// template
<wxml-to-canvas class="canvasId" height="需要高度" width="需要宽度"/>
// js
import {dom, style} from "your file path";
onLoad() {
const Com = this.setComponent(".canvasId"); // 拿到组件实例
Com.runderToCanvas({ // 这里是把你的 dom 模板渲染到组件中,并展示在页面上。
wxml: dom,
style
}) // 这里就可以看出本质上这就是配置式canvas, 定义style时也是十分麻烦的
Com.then(res => {
// 渲染成功
})
}
canvas
使用canvasAPI进行定义,我个人也是倾向于这个方案,首先不要被canvas吓住了,它本身十分简单,特别是在定义海报这个需求下,我们用到的api屈指可数。
- 优点: 自定义能力强,生成快速,无需关注第三方插件使用方法,并且会了canvas是在前端所有领域都通用的。
- 缺点:api让人望而生畏,但是请相信我canvas不难,非常简单,除非你要定义特别酷炫复杂的动画以及渲染效果。
首先我们来了解canvas的基础概念,这个比看api重要的多,canvas是一个图片,而api是在这个图片上作画,我们如何调用就相当于如何下笔。所以canvas中最重要的一点就是明白在哪里下笔,下笔的内容是什么。你所有的需求都是可以映射成我在哪里下笔,下笔内容是什么。
想象你有一个宽高400的画布,你可以在这个画布上任意作画,写字,画图,画线条等等。uniapp canvas API
// dom 定义canvas容器(画布)
<view>
<canvas canvas-id="canvasShareId" style="width: 400px; height: 400px"></canvas>
</view>
// 初始化
onLoad() {
const canvasInstance = uni.createCanvasContext("canvasShareId", this);
canvasInstance.fillStyle = "red"; // 画笔颜色
canvasInstance.setFontSize(20); // 字体大小 你的笔现在书写20px文字
canvasInstance.fillText("绘制的文本内容", 0, 30); // 在画布上写字 下笔的内容 下笔的坐标
canvasInstance.draw(); // 绘制内容 render
// 你要记住你的画笔如果在同一个位置绘画多次,那么前面的内容会被后绘制的内容给覆盖。参考现实中作画。
}
绘制海报图
我项目中的海报绘制需求十分简单,只需要背景图+小程序码合二为一即可,无需自定义标题名描述文字等等。就算你的项目中有这些需求,在你看过上一节的内容后对你来说也不是难题。
注意: canvasInstance.drawImage 绘制图片时本地资源可直接引用("/static/images/xx.png"),如果是网络图片则需要先下载,下载成功后得到本地临时地址再传入。downloadFile下载文件需要在小程序后台配置下载域名, 否则会下载失败。
// template
<view class="share-box">
<view class="switch-tab-box">
<u-tabs :list="imgList" @click="switchImg"></u-tabs>
</view>
<view class="canvas-box">
<canvas canvas-id="canvasShareId" style="width: 400px; height: 400px"></canvas>
</view>
<view class="footer-btn">
<u-button @click="downLoad" type="primary">保存图片</u-button>
</view>
</view>
// js
data() {
return {
imgList:[{ name: "五粮液", url: "https://xx.aliyuncs.com/xxx.jpg" }],
canvasInstance: null,
currentImg: null, // 当前点击项
QrUrl: "", // 向后端请求小程序码(生成的小程序码中含有动态参数 scene: shareId)
}
},
onLoad() {
this.init();
},
methods: {
async init() {
const QrUrl = await getCurrentUserQrCode({ data: { id: userId } });
if (QrUrl) {
this.canvasInstance = uni.createCanvasContext("canvasShareId", this);
this.currentImg = this.currentImg || this.imgList[0]; // 如果没有值, 直接取第一位
this.QrUrl = QrUrl;
this.drawBackgroundImage();
}
},
drawBackgroundImage() { // 绘制背景图
uni.showLoading({title: "loading...", icon: "none"});
uni.downloadFile({
url: this.currentImg.url, // https图片要在微信后台配置downloadFile合法域名
success: (res) => {
if (res) {
this.canvasInstance.drawImage(
res.tempFilePath, // 图片源 本地资源直接传入路径 "/static/images/xx.png"
0, // 图像的左上角在目标canvas上 X 轴的位置
0, // 图像的左上角在目标canvas上 Y 轴的位置
400, // 在目标画布上绘制图像的宽度,允许对绘制的图像进行缩放
400 // 在目标画布上绘制图像的高度,允许对绘制的图像进行缩放
)
this.drawQrImage(); // 绘制小程序码
}
}
});
}
},
drawQrImage() { // 绘制二维码
uni.downloadFile({
url: this.QrUrl,
success: (res) => {
if (res.statusCode === 200) {
this.canvasInstance.drawImage(
res.tempFilePath, // 图片源
60, // 图像的左上角在目标canvas上 X 轴的位置
274, // 图像的左上角在目标canvas上 Y 轴的位置
100, // 在目标画布上绘制图像的宽度,允许对绘制的图像进行缩放
100 // 在目标画布上绘制图像的高度,允许对绘制的图像进行缩放
)
this.canvasInstance.draw(); // 绘制内容到dom中
}
},
complete() {
uni.hideLoading();
}
});
},
保存海报图到本地
海报图绘制出来后都会有保存按钮,直接将canvas转换成图片下载保存到手机。下载步骤比较繁琐,需要授权和边界处理。
downLoad() {
uni.showLoading({ title: "loading...", icon: "none"});
uni.canvasToTempFilePath({
x: 0, // 画布x轴起点
y: 0, // 画布y轴起点
width: 1200,
height: 1200,
destWidth: 1200, // 输出图片宽度(默认为 width * 屏幕像素密度)
destHeight: 1200, // 输出图片高度(默认为 height * 屏幕像素密度)
quality: 1, // 图片的质量,取值范围为 (0, 1],不在范围内时当作1.0处理
canvasId: "canvasShareId",
success: function(res) {
// 在H5平台下,tempFilePath 为 base64
// console.log(res.tempFilePath)
uni.saveImageToPhotosAlbum({
filePath: res.tempFilePath, // 图片文件路径,临时文件路径或者是永久文件路径,不支持网络图片路径
success: function() {
setTimeout(() => { // success 触发 => complete 触发(hideLoading 会关闭 showToast) => 所以使用异步;
uni.showToast({
title: "下载成功!",
duration: 2000
});
}, 100)
},
fail() { // 未开启授权
uni.authorize({
scope: "scope.writePhotosAlbum", // 发起授权
success() {
uni.showToast({
title: "授权成功,请重新下载!",
icon: "none",
duration: 2000
});
},
// 如果以前拒绝过这个授权 那么直接进入 fail 回调 使用 openSetting 打开设置进行重新授权
fail() {
uni.showToast({
title: "请允许保存图片授权!",
icon: "none",
duration: 2000
});
uni.openSetting({
success() {
uni.showToast({
title: "授权成功,请重新下载!",
icon: "none",
duration: 2000
});
},
fail() {
uni.showToast({
title: "授权失败",
icon: "none",
duration: 2000
});
}
})
}
})
},
complete() {
uni.hideLoading();
}
});
}
})
}
成果展示
结语
生成海报图的总结就到这里结束了,从上面每一节的分析发现canvas生成海报是非常简单的一件事。相信你可以的。最后的图片下载也是非常简单,本质上就用了两个操作,canvas转换成可保存的临时文件路径(canvasToTempFilePath),保存文件到相册(saveImageToPhotosAlbum)。