需求:
将背景图片和生成的二维码图片合并成一张图片,可以让用户下载该图片
效果:
话不多说直接上代码
- 首先需要再页面定义一个canvas容器
<button bindtap="generatePosters">点击生成海报</button>
<block wx:if="{{isFlag}}">
<canvas type="2d" id="myCanvas" class="myCanvas"></canvas>
<!-- 下载图片按钮 -->
<button bindtap="addImage" class="add_btn">下载图片</button>
</block>
- 然后就是js部分
Page({
data: {
imageUrl: 'https://img2.baidu.com/it/u=1884974821,3634354042&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=679',
title: '扫描二维码,加入我们,新人优惠,特价商品',
code: '/static/1686968496385.jpg',
isFlag: false
},
generatePosters() {
this.setData({
isFlag: !this.data.isFlag
})
const query = wx.createSelectorQuery()
query.select('#myCanvas')
.fields({
node: true,
size: true
})
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
// 获取像素比
const dpr = wx.getSystemInfoSync().pixelRatio
// 将画布大小按照设备像素比例进行调整,使其在不同设备上显示的效果更加一致。
canvas.width = res[0].width * dpr
canvas.height = res[0].height * dpr
ctx.scale(dpr, dpr)
// 设置背景图片
const bg = canvas.createImage()
bg.src = this.data.imageUrl
bg.onload = () => {
ctx.drawImage(bg, 0, 0, 300, 400)
}
// 绘制 底部 盒子
ctx.fillStyle = '#f0f0f0'
ctx.fillRect(0, 400, 500, 100)
ctx.fill()
// 设置 字体
ctx.fillStyle = '#000'
ctx.font = "16px 楷体"
ctx.fillText(this.data.title, 12, 450)
ctx.fill()
// 生成 二维码
const code = canvas.createImage()
code.src = this.data.code
code.onload = () => {
ctx.drawImage(code, 220, 420, 60, 60)
}
this.setData({
canvas: canvas
})
})
},
})
- 做好这些后我们会发现在canvas中绘制的文字是不会换行的,需要我们自己去写,具体该函数怎么用看最后总代码
/**
* 参数解析:
* ctx: canvas绘图上下文
* str: 需要绘制的文本内容
* draw_width: 绘制后的文字显示宽度
* lineNum: 最大行数,多出部分用'...'表示, 如果传-1可以达到自动换行效果
* startX: 绘制文字的起点 X 轴坐标
* startY: 绘制文字的起点 Y 轴坐标
* steps: 文字行间距
*/
toFormateStr(ctx, str, draw_width, lineNum, startX, startY, steps) {
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 - 5) + '...';
ctx.fillText(keyStr, startX, startpoint);
break;
}
startpoint = startpoint + steps;
}
}
}
- 然后就是下载图片下载图片
// 下载图片
addImage() {
wx.canvasToTempFilePath({
canvas: this.data.canvas, // canvas 实例
success(res) {
console.log(res);
// canvas 生成图片成功
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success(res) {
// 保存成功
wx.showToast({
title: '已保存相册',
icon: 'success',
duration: 2000
})
}
})
}
})
}
- 完整代码
Page({
data: {
imageUrl: 'https://img2.baidu.com/it/u=1884974821,3634354042&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=679',
title: '扫描二维码,加入我们,新人优惠,特价商品',
code: '/static/1686968496385.jpg',
isFlag: false
},
generatePosters() {
this.setData({
isFlag: !this.data.isFlag
})
const query = wx.createSelectorQuery()
query.select('#myCanvas')
.fields({
node: true,
size: true
})
.exec((res) => {
const canvas = res[0].node
const ctx = canvas.getContext('2d')
const dpr = wx.getSystemInfoSync().pixelRatio
// 将画布大小按照设备像素比例进行调整,使其在不同设备上显示的效果更加一致。
canvas.width = res[0].width * dpr
canvas.height = res[0].height * dpr
ctx.scale(dpr, dpr)
const bg = canvas.createImage()
bg.src = this.data.imageUrl
bg.onload = () => {
ctx.drawImage(bg, 0, 0, 300, 400)
}
// 绘制 底部 盒子
ctx.fillStyle = '#f0f0f0'
ctx.fillRect(0, 400, 500, 100)
ctx.fill()
// 设置 字体
ctx.fillStyle = '#000'
ctx.font = "16px 楷体"
this.toFormateStr(ctx, this.data.title, 160, 5, 12, 450, 20)
// 生成 二维码
const code = canvas.createImage()
code.src = this.data.code
code.onload = () => {
ctx.drawImage(code, 220, 420, 60, 60)
}
this.setData({
canvas: canvas
})
})
},
/**
* 参数解析:
* ctx: canvas绘图上下文
* str: 需要绘制的文本内容
* draw_width: 绘制后的文字显示宽度
* lineNum: 最大行数,多出部分用'...'表示, 如果传-1可以达到自动换行效果
* startX: 绘制文字的起点 X 轴坐标
* startY: 绘制文字的起点 Y 轴坐标
* steps: 文字行间距
*/
toFormateStr(ctx, str, draw_width, lineNum, startX, startY, steps) {
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 - 5) + '...';
ctx.fillText(keyStr, startX, startpoint);
break;
}
startpoint = startpoint + steps;
}
}
},
// 下载图片
addImage() {
wx.canvasToTempFilePath({
canvas: this.data.canvas, // canvas 实例
success(res) {
console.log(res);
// canvas 生成图片成功
wx.saveImageToPhotosAlbum({
filePath: res.tempFilePath,
success(res) {
// 保存成功
wx.showToast({
title: '已保存相册',
icon: 'success',
duration: 2000
})
}
})
}
})
}
})
.fs