logo头像
游魂的网络日志

小程序canvas生成海报图片压缩和失真问题解决

本文于456天之前发表,文中内容可能已经过时。

微信小程序实现canvas按照原图等比例不失真绘制海报图,防止模糊

我这里的场景是收款二维码+收款背景图。

绘制二维码

我这里绘制二维码使用的 wxapp-qrcode ,也可以使用weapp-qrcode,基本都一样,详细代码不过多累赘,这里主要说下适配不同屏幕大小的canvas。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
onLoad: function (options) {
const size = this.setCanvasSize() // 动态设置画布大小
this.createQrCode('www.iyouhun.com', "mycanvas", size.w, size.h)
},
// 适配不同屏幕大小的canvas
setCanvasSize: function () {
const size = {}
try {
var res = wx.getSystemInfoSync()
var scale = 750 / 686; // 不同屏幕下canvas的适配比例;设计稿是750宽,686是因为wxss样式文件中设置的canvas大小
var width = res.windowWidth / scale
var height = width; // canvas画布为正方形
size.w = width
size.h = height
} catch (e) {
console.log("获取设备信息失败" + e)
}
return size
},

绘制背景图

动态设置图片的高度和宽度

在小程序布局中,如果图片不是固定高度和高度,但image设置的是固定的高度和宽度,这时候原始图片相对image设置的固定高度和宽度不是等比例大小,那么这张图片就会变形,变的不清晰。这时就可以使用下面的等比例缩放的方式缩放图片,让图片不变形。或者通过imagebindload方法动态的获取图片的高度和宽度,动态的设置图片的高度和宽度,是图片布局的高度和宽度和原始图片的高度和宽度相等。

图片等比例缩放工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
//Util.js 

class Util{
/***
* 按照显示图片的宽等比例缩放得到显示图片的高
* @params originalWidth 原始图片的宽
* @params originalHeight 原始图片的高
* @params imageWidth 显示图片的宽,如果不传就使用屏幕的宽
* 返回图片的宽高对象
***/
static imageZoomHeightUtil(originalWidth,originalHeight,imageWidth){
let imageSize = {};
if(imageWidth){
imageSize.imageWidth = imageWidth;
imageSize.imageHeight = (imageWidth * originalHeight) / originalWidth;
}else{//如果没有传imageWidth,使用屏幕的宽
wx.getSystemInfo({
success: function (res) {
imageWidth = res.windowWidth;
imageSize.imageWidth = imageWidth;
imageSize.imageHeight = (imageWidth * originalHeight) / originalWidth;
}
});
}
return imageSize;
}

/***
* 按照显示图片的高等比例缩放得到显示图片的宽
* @params originalWidth 原始图片的宽
* @params originalHeight 原始图片的高
* @params imageHeight 显示图片的高,如果不传就使用屏幕的高
* 返回图片的宽高对象
***/
static imageZoomWidthUtil(originalWidth,originalHeight,imageHeight){
let imageSize = {};
if(imageHeight){
imageSize.imageWidth = (imageHeight *originalWidth) / originalHeight;
imageSize.imageHeight = imageHeight;
}else{//如果没有传imageHeight,使用屏幕的高
wx.getSystemInfo({
success: function (res) {
imageHeight = res.windowHeight;
imageSize.imageWidth = (imageHeight *originalWidth) / originalHeight;
imageSize.imageHeight = imageHeight;
}
});
}
return imageSize;
}

}

export default Util;

工具库使用

1
<image bindload="imageLoad"  src="../test.png"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Util from '../common/Util'

Page({
data:{
imageWidth:0,
imageHeight:0
},
imageLoad: function (e) {
//获取图片的原始宽度和高度
let originalWidth = e.detail.width
let originalHeight = e.detail.height
let imageSize = Util.imageZoomWidthUtil(originalWidth,originalHeight,145)
this.setData({imageWidth:imageSize.imageWidth,imageHeight:imageSize.imageHeight})
}
})

绘制背景图

用上面的方法动态设置图片宽高,解决失真问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Util from '../../libs/Util'
// 背景图
let bgImg = new Promise(function (resolve) {
wx.getImageInfo({
src: 'https://www.iyouhun.com/payment/payment_pic.jpg',
success: function (res) {
that.setData({
imgInfo: res
})
// 根据屏幕宽度得到图片高
const imageSize = Util.imageZoomHeightUtil(that.data.imgInfo.width, that.data.imgInfo.height)
that.setData({ canvasHeight: imageSize.imageHeight })
resolve(res.path)
},
fail: function (err) {
console.log(err)
wx.showToast({
title: '网络错误请重试',
icon: 'loading'
})
}
})
})

合成海报/收款码

这里使用Promise分别去绘制二维码和背景图。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 收款码
let qrcodeImg = new Promise(function (resolve) {
// ...
})
// 背景图
let bgImg = new Promise(function (resolve) {
// ...
}
Promise.all([bgImg, qrcodeImg]).then(function(result) {
wx.showLoading({title: '加载中'})
// canvas绘制文字和图片,创建画图
const ctx = wx.createCanvasContext('myCanvas')
// 绘制背景图
ctx.drawImage(result[0], 0, 0, that.data.imgInfo.width, that.data.imgInfo.height, 0, 0, that.data.canvasWidth, that.data.canvasHeight)
ctx.setFillStyle('white')
// 绘制二维码 二维码宽度300
const qrX = (that.data.canvasWidth - 300) / 2 // canvas宽度 - 二维码宽度 / 2 (居中)
ctx.drawImage(result[1], qrX, 120, 300, 300)
// 绘制文本
ctx.fillStyle = '#ffffff' // 背景
ctx.fillRect(Math.floor(qrX),420,300,20)
ctx.fillStyle = "#333333"
ctx.font = 25 + 'px Arial' // 文本大小, 字体
ctx.textAlign = 'center'
ctx.fillText(
'No.'+ that.data.serialNum,
that.data.canvasWidth / 2, // 左上角 X坐标
430, // 左上角 Y坐标
300
)
//canvasToTempFilePath必须要在draw的回调中执行,否则会生成失败,官方文档有说明
ctx.draw(false, setTimeout(function () {
wx.canvasToTempFilePath({
canvasId: 'myCanvas',
x: 0,
y: 0,
width: that.data.canvasWidth,
height: that.data.canvasHeight,
success: function (res) {
wx.hideLoading()
that.setData({
qrcodeStatus: true,
shareImgSrc: res.tempFilePath
})
},
fail: function (res) {
wx.hideLoading()
wx.showToast({
title: '生成失败',
icon: "none"
})
}
})
}, 1000))
})

效果图