微信小程序将页面保存成图片
前言:这玩意是真的坑
开发环境
原型图
代码实现(各种踩坑)
页面具体怎么画的这里就不多说了,具体讲保存图片这个功能,一看到这个功能就觉得似曾相识,我以前好像做过,立马想到了html2cavas这个插件,开开心心的把这玩意下载下来。这玩意怎么用我在《将页面元素转为canvas并导出为PDF文件》里面有讲到过,它需要传入一个原生DOM元素,它会把这个DOM绘制成canvas并且返回出来。
这个时候第一个坑来了
当使用document.querySelector
的时候,直接报错了。
翻阅文档发现,小程序不支持上面那种方式获取DOM,需要使用小程序提供的api来获取。
但是使用该api获取到的并不是原生DOM,翻阅了很多资料也没找到解决方案,因此我就放弃了使用html2canvas这个插件。
使用chameleon多态协议调用wx.createCanvasContext
我只能老老实实的手动去把这个页面用canvas画出来,微信小程序提供了画布的相关api,但是呢,chameleon框架并不能直接访问这些api,它需要使用一个被称作多态协议的一个东西。
在src/component/html2image/
下新建index.interface
<script cml-type="interface">
interface html2ImageInterface {
saveImage(title: string, code: string, desc: string, img: string): void;
}
</script>
<script cml-type="wx">
class Method implements html2ImageInterface {
/**
*
* @param title 标题
* @param code 合同号
* @param desc 描述
* @param img 二维码
*/
saveImage(title, code, desc, img) {
const ctx = wx.createCanvasContext('qrcode');
// 设置字体
ctx.setFontSize(36)
// 设置填充色
ctx.setFillStyle('#F9F9F8')
// 绘制矩形
ctx.fillRect(0, 0, 500, 700)
ctx.setFillStyle('#000')
// 设置文字对齐方式
ctx.setTextAlign('center')
// 绘制文字
ctx.fillText(title, 250, 100)
ctx.fillText(code, 250, 180)
// 绘制图片 这里的img是base64字符串
ctx.drawImage(img, 125, 250, 250, 250)
ctx.fillText(desc, 250, 580)
// 开始绘制
ctx.draw(false)
}
}
export default new Method();
</script>
在src/pages/demo.cml
页面放入一个canvas标签
<template>
<button btn-style="background-color:#118EE9" style="margin: 0 auto" type="blue" size="big" text="保存图片" c-bind:onclick="saveImage"></button>
<canvas style="width: 500px; height: 700px;" canvas-id="qrcode" />
</template>
在保存图片按钮中调用多态协议中的方法,因为canvas只是用来转图片的,所以并不需要把它显示在页面上,页面上还是展示我用view画的样式,所以把它隐藏掉即可。
import html2ImageUtil from '../../../components/QuerySelector/index.interface'
saveImage() {
html2ImageUtil.saveImage(this.title, this.code, this.description, this.imageSrc)
}
将canvas转为图片并保存
将canvas转为图片需要用到wx.canvasToTempFilePath
这个api,还是在index.interface
里进行改动。canvas的draw方法里提供一个回调函数,可以在这里面进行转图片
<script cml-type="interface">
interface html2ImageInterface {
saveImage(title: string, code: string, desc: string, img: string): void;
}
</script>
<script cml-type="wx">
class Method implements html2ImageInterface {
/**
*
* @param title 标题
* @param code 合同号
* @param desc 描述
* @param img 二维码
*/
saveImage(title, code, desc, img) {
//此处省略绘制过程
// 开始绘制
ctx.draw(false, () => {
() => {
wx.canvasToTempFilePath({
width: 500,
height: 700,
destWidth: 500,
destHeight: 700,
canvasId: 'qrcode',
success(res) {
// 绘制成功会返回一个res.tempFilePath,就是生成的图片路径,等下需要用到这个
})
})
}
}
export default new Method();
</script>
转成图片并且拿到图片路径之后,我们就可以把图片保存至相册了。要想保存至相册需要用户同意微信小程序访问相册的权限。
<script cml-type="interface">
interface html2ImageInterface {
saveImage(title: string, code: string, desc: string, img: string): void;
}
</script>
<script cml-type="wx">
class Method implements html2ImageInterface {
/**
*
* @param title 标题
* @param code 合同号
* @param desc 描述
* @param img 二维码
*/
saveImage(title, code, desc, img) {
//此处省略绘制过程
// 开始绘制
ctx.draw(false, () => {
() => {
wx.canvasToTempFilePath({
width: 500,
height: 700,
destWidth: 500,
destHeight: 700,
canvasId: 'qrcode',
success(res) {
// 绘制成功会返回一个res.tempFilePath,就是生成的图片路径,等下需要用到这个
const { tempFilePath } = res
wx.getSetting({
success(res2) {
// 判断用户是否授权微信小程序访问相册
if (!res2.authSetting['scope.writePhotosAlbum']) {
// 没有则发起授权弹窗
wx.authorize({
scope: "scope.writePhotosAlbum",
success () {
// 授权后将图片保存至相册
wx.saveImageToPhotosAlbum({
filePath: tempFilePath,
success() {
// 保存成功
}
})
}
})
} else {
// 授权后将图片保存至相册
wx.saveImageToPhotosAlbum({
filePath: tempFilePath,
success() {
// 保存成功
}
})
}
}
})
})
})
}
}
export default new Method();
</script>
第二个坑来咯
到这里似乎蛮顺利的,试一下,我的mac可以弹出保存窗口了。
而且保存下来的图片也挺正常的。
可以开开心心的进行真机调试了,当我打开手机,进入页面,保存图片,一切非常顺利,然后当我打开相册我人傻了。
???就邪门,二维码咋没了。难道是我姿势不对?然后我又单手倒立试了一次,依然是没有二维码,看来跟我姿势没关系。作为一名自身的百度工程师,最后还是被我查到了原因。
base64直接在canvas上绘制,在真机上是显示不出来的。。。。。。
这种情况解决办法有两种:
- 让后端直接返回图片url,使用
wx.getImageInfo
将图片的网络地址缓存到本地,它会返回一个本地地址,然后使用这个地址把图片绘制到canvas上面。 - 依然使用base64,手动把base64转成图片并存到本地,在拿这个本地地址去绘制到canvas上面。
这里我用的第二种,因为我懒得去麻烦后端了。要在ctx.drawImage(img, 125, 250, 250, 250)
之前,处理下这个img。
<script cml-type="interface">
interface html2ImageInterface {
saveImage(title: string, code: string, desc: string, img: string): void;
}
</script>
<script cml-type="wx">
class Method implements html2ImageInterface {
/**
*
* @param title 标题
* @param code 合同号
* @param desc 描述
* @param img 二维码
*/
saveImage(title, code, desc, img) {
// 此处省略绘制图片之外的过程
// 文件管理对象
const fs = wx.getFileSystemManager();
// 分别获取图片后缀和图片base64内容
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(img) || [];
const times = new Date().getTime();
// 自定义本地图片路径 时间戳+图片后缀
const imgSrc = wx.env.USER_DATA_PATH + '/' + times + '.' + format;
// 将Base64字符串转成ArrayBuffer对象
const buffer = wx.base64ToArrayBuffer(bodyData);
// 保存文件
fs.writeFile({
filePath: imgSrc,
data: buffer,
encoding: 'binary',
success() {
// 绘制图片 这里不再使用base64,直接传入上面生成的imgSrc
ctx.drawImage(imgSrc, 125, 250, 250, 250)
// 此处省略将canvas转为图片并保存过程
})
}
}
export default new Method();
</script>
再次真机调试,一切ok。
总结
微信开发者工具真垃圾!!!