需要完成的需求:
一张图片上面有二维码、头像、文字 头像、文字都是从接口拿、二维码是根据不同的业务场景达到分享的目的
需要解决的问题:
如何通过wx.saveImageToPhotosAlbum把需要的东西都能生成出来
解决的思路:
通过小程序canvas 画图把对应的二维码和头像和文字整合到一个图片上再通过wx.saveImageToPhotosAlbum保存下来
解决方案:
1:首先页面有一个
<canvas></canvas>标签去放入需要wx.saveImageToPhotosAlbum保存的图片 2:生成的二维码: 可能生成小程序码、也可能生成二维码(跳转h5)、二者都有就需要二个<canvas></canvas>标签
实现方式:
小程序的
<canvas></canvas>换成用type=2d模式 以前的模式不在维护了<canvas> type="2d" id="canvas" style="这里写自己需要的海报大小"></canvas>这个canvas标签相当于对应的海报的格式, 然后就是获取这个标签
query.select('#myQrcode')
.fields({
node: true,
size: true
})
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio // 这边的dpr主要给生成canvas时候使用
ctx.scale(dpr, dpr)
// 可以在这里定义一个posterDatas 把这个canvas、ctx、dpr 存进去
// 获取背景图 可以定义个promise
const promise1 = new Promise(function (resolve, reject) {
const posterBg = canvas.createImage();
posterBg.src = "自己需要的图片";
posterBg.onload = (e) => {
resolve(posterBg);
}
});
Promise.all(promise1).then(res => {
// 这边都可以去绘制对应的图片了
ctx.drawImage(res[0], 0, 0, "初始化图片的宽度", "初始化图片的高度"); // (图片,左距离,上距离,宽(px as Number),高(px as Number))
// 这时候都可以通过wx.canvasToTempFilePath 保存canvas 了然后通过success里面的tempFilePath去在 wx.saveImageToPhotosAlbum里面保存图片了
一个背景图的海报都生成了 下面开始第二个
})
})
现在我们需要把二维码也放上去 这时候生成 小程序码是属于简单的情况 这个一般由公司后端去调小程序现有的接口 然后给你个data值
const query = wx.createSelectorQuery()
query.select('自己定义的小程序的canvas')
.fields({
node: true,
size: true
})
.exec((res) => {
const miniCanvas = res[0].node
const miniCtx = miniCanvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio
// 这一套其实都和前面获取海报图一样
miniCtx.scale(dpr, dpr);
getMiniQrUrl("接口需要的参数").then((res) => {
let promise1 = new Promise((resolve, reject) => {
const photo = miniCanvas.createImage();
photo.src = "接口需要的二维码";
photo.onload = (e) => {
resolve(photo);
}
});
Promise.all([promise1]).then(res => {
// 这里可以在这里定义 也可以下面用的时候写死 看个人习惯
let miniUrl_width = 70; // 二维码的宽度
let miniUrl_heigth = 70; // 二维码的高度
let miniUrl_x = 0; // 二维码距离canvas的x轴距离
let miniUrl_y = 0; // 二维码距离canvas的y轴距离
// 下面这几部是把小程序二维码变为圆形 不需要可以跳过
// start
miniCtx.save(); // 开始绘制 不写好像也可以
// 这个可以看小程序文档写的 比较清晰 主要是为了画圆
miniCtx.arc(miniUrl_width / 2 + miniUrl_x, miniUrl_heigth / 2 + miniUrl_y, miniUrl_width / 2, 0, Math.PI * 2, false);
// 裁切
miniCtx.clip()
// end
miniCtx.drawImage(res[0], miniUrl_x, miniUrl_y, miniUrl_width, miniUrl_heigth);
wx.canvasToTempFilePath({
// 把当前画布指定区域的内容导出生成指定大小的图片
canvas: "自己定义的ID", // canvas实例
success: res => {
this.setData({
miniCodeUrl: res.tempFilePath,
//我们当这个url为miniCodeUrl 这边拿到图片路径存在自己的data里面 因为还需要把这个图片放在海报图中
})
}
})
})
})
})
}
const query = wx.createSelectorQuery()
query.select('#myQrcode')
.fields({
node: true,
size: true
})
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio // 这边的dpr主要给生成canvas时候使用
ctx.scale(dpr, dpr)
// 可以在这里定义一个posterDatas 把这个canvas、ctx、dpr 存进去
// 获取背景图 可以定义个promise
const promise1 = new Promise(function (resolve, reject) {
const posterBg = canvas.createImage();
posterBg.src = "自己需要的图片";
posterBg.onload = (e) => {
resolve(posterBg);
}
});
// 这时候我们有小程序码了
const promise2 = new Promise(function (resolve, reject) {
const miniCode = canvas.createImage();
miniCode.src = "小程序的url::miniCodeUrl";
miniCode.onload = (e) => {
resolve(miniCode);
}
});
Promise.all(promise1, promise2).then(res => {
// 这边都可以去绘制对应的图片了
ctx.drawImage(res[0], 0, 0, "初始化图片的宽度", "初始化图片的高度"); // (图片,左距离,上距离,宽(px as Number),高(px as Number))
ctx.drawImage(res[1], "左距离", "上距离", "宽", "高");
// 这时候都可以通过wx.canvasToTempFilePath 保存canvas 了然后通过success里面的tempFilePath去在 wx.saveImageToPhotosAlbum里面保存图片了
})
})
然后小程序二维码就画到海报图里面去了 这时候可能有的业务是别的二维码 这时候就需要使用第三方插件了
这时候github 上有一个github.com/yingye/weap… 生成小程序二维码按照文档生成就行了
但是我这边遇到个问题 用这种方式生成的二维码 在安卓手机上会有个别几个生成的二维码扫不出来 不知道什么问题 有踩坑的大佬还得请指教一下
这个时候我想到了能不能生成base64的格式再去使用 于是在网上找到了github.com/Pudon/weapp… 这个插件,但是这时候我又遇到坑了,生成得base64 再去画图时候拿不到这个链接、这时候就想去把base64再转成网络图片 然后有需求想在二维码中加一个图标的 可以和上面加二维码一样 再起一个 promise 然后放在二维码中间 下面是生成base64的二维码的方案
let linkUrl = "一般是h5页面的路径"
let base64 = QR.drawImg(linkUrl, {
typeNumber: 4,
errorCorrectLevel: 'M',
size: 500
})
// 这边拿到base64 去直接生成还是有点问题 所以我想到了把base64转为图片 再去生成
this.base64src(base64, res => {
console.log(res) // 返回图片地址,直接赋值到image标签即可
this.setData({
qrCodeImage: res
})
});
}
// 这边是网上找的base64转为图片的方法
base64src(base64data, cb) {
const fsm = wx.getFileSystemManager();
const FILE_BASE_NAME = 'code'; //自定义文件名
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
if (!format) {
return (new Error('ERROR_BASE64SRC_PARSE'));
}
const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.png`;
const buffer = wx.base64ToArrayBuffer(bodyData);
fsm.writeFile({
filePath,
data: buffer,
encoding: 'binary',
success() {
cb(filePath);
},
fail() {
return (new Error('ERROR_BASE64SRC_WRITE'));
},
});
}
const query = wx.createSelectorQuery()
query.select('#myQrcode')
.fields({
node: true,
size: true
})
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio // 这边的dpr主要给生成canvas时候使用
ctx.scale(dpr, dpr)
// 可以在这里定义一个posterDatas 把这个canvas、ctx、dpr 存进去
// 获取背景图 可以定义个promise
const promise1 = new Promise(function (resolve, reject) {
const posterBg = canvas.createImage();
posterBg.src = "自己需要的图片";
posterBg.onload = (e) => {
resolve(posterBg);
}
});
// 这时候我们有小程序码了
const promise2 = new Promise(function (resolve, reject) {
const miniCode = canvas.createImage();
miniCode.src = "小程序的url::miniCodeUrl";
miniCode.onload = (e) => {
resolve(miniCode);
}
});
const promise3 = new Promise(function (resolve, reject) {
const qrCodeImage = canvas.createImage();
qrCodeImage.src = "h5二维码的url::qrCodeImage";
qrCodeImage.onload = (e) => {
resolve(qrCodeImage);
}
});
Promise.all(promise1, promise2, promise3).then(res => {
// 这边都可以去绘制对应的图片了
ctx.drawImage(res[0], 0, 0, "初始化图片的宽度", "初始化图片的高度"); // (图片,左距离,上距离,宽(px as Number),高(px as Number))
ctx.drawImage(res[1], "左距离", "上距离", "宽", "高");
ctx.drawImage(res[3], "左距离", "上距离", "宽", "高");
// 这时候都可以通过wx.canvasToTempFilePath 保存canvas 了然后通过success里面的tempFilePath去在 wx.saveImageToPhotosAlbum里面保存图片了
})
})
这时候还有头像和问题 头像我都不说了 其实就是和生成二维码一样的模式 文字的话官网的api也很详细 文字的话主要是处理一个换行的问题下面代码片端 会有个换行的方法 也是网上找的一个方法 然后自己二次开发了一下blog.csdn.net/qq_39905409…
drawPosterCode() {
let linkUrl = "一般是h5页面的路径"
let base64 = QR.drawImg(linkUrl, {
typeNumber: 4,
errorCorrectLevel: 'M',
size: 500
})
// 这边拿到base64 去直接生成还是有点问题 所以我想到了把base64转为图片 再去生成
this.base64src(base64, res => {
console.log(res) // 返回图片地址,直接赋值到image标签即可
this.setData({
qrCodeImage: res
})
});
}
// 这边是网上找的base64转为图片的方法
base64src(base64data, cb) {
const fsm = wx.getFileSystemManager();
const FILE_BASE_NAME = 'code'; //自定义文件名
const [, format, bodyData] = /data:image\/(\w+);base64,(.*)/.exec(base64data) || [];
if (!format) {
return (new Error('ERROR_BASE64SRC_PARSE'));
}
const filePath = `${wx.env.USER_DATA_PATH}/${FILE_BASE_NAME}.png`;
const buffer = wx.base64ToArrayBuffer(bodyData);
fsm.writeFile({
filePath,
data: buffer,
encoding: 'binary',
success() {
cb(filePath);
},
fail() {
return (new Error('ERROR_BASE64SRC_WRITE'));
},
});
}
const query = wx.createSelectorQuery()
query.select('#myQrcode')
.fields({
node: true,
size: true
})
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio // 这边的dpr主要给生成canvas时候使用
ctx.scale(dpr, dpr)
// 可以在这里定义一个posterDatas 把这个canvas、ctx、dpr 存进去
// 获取背景图 可以定义个promise
const promise1 = new Promise(function (resolve, reject) {
const posterBg = canvas.createImage();
posterBg.src = "自己需要的图片";
posterBg.onload = (e) => {
resolve(posterBg);
}
});
// 这时候我们有小程序码了
const promise2 = new Promise(function (resolve, reject) {
const miniCode = canvas.createImage();
miniCode.src = "小程序的url::miniCodeUrl";
miniCode.onload = (e) => {
resolve(miniCode);
}
});
const promise3 = new Promise(function (resolve, reject) {
const qrCodeImage = canvas.createImage();
qrCodeImage.src = "h5二维码的url::qrCodeImage";
qrCodeImage.onload = (e) => {
resolve(qrCodeImage);
}
});
Promise.all(promise1, promise2, promise3).then(res => {
// 这边都可以去绘制对应的图片了
ctx.drawImage(res[0], 0, 0, "初始化图片的宽度", "初始化图片的高度"); // (图片,左距离,上距离,宽(px as Number),高(px as Number))
ctx.drawImage(res[1], "左距离", "上距离", "宽", "高");
ctx.drawImage(res[3], "左距离", "上距离", "宽", "高");
// 这时候都可以通过wx.canvasToTempFilePath 保存canvas 了然后通过success里面的tempFilePath去在 wx.saveImageToPhotosAlbum里面保存图片了
// 处理文字
ctx.font = "10px Arial"; // 文字的大小和字体
ctx.fillStyle = "#fff"; // 文字的颜色
ctx.textAlign = "left" // *** 文字的居中方式 *** 当时没设置这个 被坑了蛮久
toFormateStr(ctx, str, draw_width, lineNum, startX, startY, steps) // 然后直接调用这个方法 达到换行的需求
})
})
/*
@function 处理canvas换行
@param ctx {CanvasRenderingContext2D} canvas绘图上下文
@param str {String} 需要传入的文字
@param draw_width {Number} 绘制后的文字显示宽度
@param lineNum {Number} 最大行数,多出部分用'...'表示, 如果传-1可以达到自动换行效果
@param startX {Number} 绘制文字的起点 X 轴坐标
@param startY {Number} 绘制文字的起点 Y 轴坐标
@param steps {Number} 文字行间距
*/
toFormateStr(ctx, str, draw_width, lineNum, startX, startY, steps) {
if (str) {
// 这边是判断文字是否有换行 如果有换行都按照换行 业务处理方式
if (str.indexOf("\n") != -1) {
let s = str.split("\n");
let ss = s.reverse()
for (let i = 0; i < ss.length; i++) {
ctx.fillText(s[i], startX, startY - steps * i, 120);
}
} else {
let strWidth = ctx.measureText(str).width; // 测量文本源尺寸信息(宽度)
let startpoint = startY,
keyStr = '',
sreLN = strWidth / draw_width;
let liner = Math.ceil(sreLN); // 计算文本源一共能生成多少行
let strlen = parseInt(str.length / sreLN); // 等比缩放测量一行文本显示多少个字符
// 若文本不足一行,则直接绘制,反之大于传入的最多行数(lineNum)以省略号(...)代替
if (strWidth < draw_width) {
ctx.fillText(str, startX, startpoint);
} else {
for (let i = 1; i < liner + 1; i++) {
let startPoint = strlen * (i - 1);
if (i < lineNum || lineNum == -1) {
keyStr = str.substr(startPoint, strlen);
ctx.fillText(keyStr, startX, startpoint);
} else {
keyStr = str.substr(startPoint, strlen - 1) + '...';
ctx.fillText(keyStr, startX, startpoint);
break;
}
startpoint = startpoint + steps;
}
}
}
}
},
写的有点乱,实在文笔不太好,有大佬还得需指点指点