I want to store several blobs from canvas elements using canvas.toBlob function to an array.
I've tried the code below which was useless. The result showed that the .toBlob() function works like an async function, and I have no knowledge about it.
function saveAllPics(node_list) {
// convert canvas element to png
let pics = [];
for (let canvas of node_list) {
canvas.toBlob(function(blob) {
pics.push(blob);
console.log(pics);
});
}
let zipPics = function(pics) {
// compress all pics into a zip file
let zip = new JSZip();
console.log("pics length:", pics.length);
for (let i = 0; i < pics.length; i++) {
let pic_data = pics[i];
console.log(i);
console.log(pic_data);
zip.file("pic-" + String(i) + ".png", pic_data, { binary: true });
}
console.log(zip);
}
setTimeout(function(pics) { zipPics(pics); }, 2000);
And the result:
pics length: 0
{files: {…}, comment: null, root: '', clone: ƒ}
[Blob]
(2) [Blob, Blob]
(3) [Blob, Blob, Blob]
(4) [Blob, Blob, Blob, Blob]
(5) [Blob, Blob, Blob, Blob, Blob]
(6) [Blob, Blob, Blob, Blob, Blob, Blob]
(7) [Blob, Blob, Blob, Blob, Blob, Blob, Blob]
(8) [Blob, Blob, Blob, Blob, Blob, Blob, Blob, Blob]
My final goal is to collect several canvas elements, then merge them into a PDF file (in a user script). However, I have found I was unable to make a PDF, so I decided to use Node.js.
Then, I decided to take it a step further: download the PNGs and ask the users to use another Python script to merge them to a PDF file. That's why I need a zip containing these blobs.
The result showed that the toBlob function seems like an async function, and I have no knowledge about it.
Yeah, toBlob() accepts a callback, and you're using it now in your code. But, we can easily clean this up using async, await, and Promises. I would do this all in the same loop. Untested code, but try something like this:
async function saveAllPics(canvases) {
const zip = new JSZip();
for (const [i, canvas] of canvases.entries()) {
zip.file(
// File name
`pic-${i}.png`,
// File data
await new Promise((resolve, reject) => {
// If a blob is returned, success!... otherwise reject.
canvas.toBlob(blob => blob ? resolve(blob) : reject());
}),
// JSZip Options
{ binary: true }
);
}
return zip;
}
By the way, my final goal is to collect several canvas elements then merge them to a pdf file (In a user script).
Have you considered one of the various JavaScript libraries for creating PDFs client-side? For example, jsPDF: https://github.com/parallax/jsPDF
I found that canvas.toDataURL helped a lot.
/**
* store all canvas imgs as pngs into a zip file
* @param {Array} node_list canvas元素列表
*/
function saveAllPics(node_list) {
let zip = new JSZip();
let n = node_list.length;
for (let i = 0; i < n; i++) {
let canvas = node_list[i];
let data_base64 = canvas.toDataURL();
let blob = atob(data_base64.split(",")[1]);
zip.file(`page-${i}.png`, blob, { binary: true });
}
// promise.then(onCompleted, onRejected);
zip.generateAsync({ type: "blob" }).then(function(content) {
// see filesaver.js
console.log(content);
saveAs(content, "pics.zip");
});
}