利用canvas拼接图片并导出或上传

image.png

生成jpg

不使用 png 是因为背景透明,导出效果不太好,canvas 转换成 jpeg 之前会移除alpha通道,所以透明区域被填充成了黑色

toDataURL

canvas.toDataURL(type, encoderOptions);
type: 图片格式,默认为 image/png
encoderOptions: 在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。

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
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* 合并多张图片生成 一张 jpg 图片
* @param {*} charts 一维数组
* @param {*} outCanvasId 隐藏于视线之外 canvas的 id
* @param {*} direction vertical | horizontal 按方向拼接图片
*/
export async function getChartImage(
charts,
outCanvasId,
drawHorizontal = false
) {
if (!Array.isArray(charts) || charts.length === 0 || !outCanvasId) return {};
const arr = charts.map(item => {
if ("toDataURL" in item) {
return {
base64Str: item.toDataURL("image/jpeg"),
width: item.width,
height: item.height,
};
}
return item;
});
const offsetY = 30; // 每张图片的相互间隔距离
const offsetX = 20; // 横向图片排布时的间隔距离

const width = drawHorizontal
? arr.reduce((res, cur) => {
return res + cur.width;
}, (arr.length - 1) * offsetX)
: Math.max(...arr.map(v => v.width));
const height = drawHorizontal
? Math.max(...arr.map(v => v.height))
: arr.reduce((res, cur) => {
return res + cur.height;
}, (arr.length - 1) * offsetY);

// 异步代码这里要阻塞执行
const imgs = await Promise.all(
arr.map(v => {
return new Promise(res => {
const img = new Image();
img.src = v.base64Str;
img.addEventListener("load", () => {
res(img);
});
});
})
);

const canvas = document.getElementById(outCanvasId);
const context = canvas.getContext("2d");

// context.clearRect(0, 0, width, height); // 宽高被重设时会自动清空画布
canvas.width = width;
canvas.height = height;

context.fillStyle = "#fff";
context.fillRect(0, 0, width, height);
imgs.forEach((img, index) => {
const idx = index && index - 1;
if (drawHorizontal) {
context.drawImage(img, index * (imgs[idx].width + offsetX), 0);
} else {
context.drawImage(img, 0, index * (imgs[idx].height + offsetY));
}
});

// toBlob(callback,"image/jpeg") 此方法时异步的
const newBase64 = canvas.toDataURL("image/jpeg", 0.9);
return {
base64Str: newBase64,
width,
height,
};
}

可以多次调用自己 以达到横向或竖向拼接图片的目的

注意:不考虑兼容性的情况下可直接使用toBlob转二进制数据传给后端,少写下面一部分代码

fetch上传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
export function uploadImage(base64String, fileName) {
return new Promise(resolve => {
// 下面将要把 base64 转换成formdata
// 这里对base64串进行操作,去掉url头,并转换为byte
const bytes = window.atob(base64String.split(",")[1]);
const arr = [];
const bytesLength = bytes.length;
for (let i = 0; i < bytesLength; i += 1) {
arr.push(bytes.charCodeAt(i));
}
const blob = new Blob([new Uint8Array(arr)], { type: "image/jpeg" });

const formData = new FormData();
// 后端接收二进制数据
formData.append("file", blob, `${fileName}.jpg`);
fetch("http://localhost/xxxx",{
method:"POST",
body:formdata
}).then(function(res){
if (res.code === 0) {
resolve(res.data.file_id);
})
});
}

可参考