Reputation: 440
I am attempting to download a large canvas image (several thousand pixels height and width) on the click of a button using toBlob
in the following code, which doesn't seem to work:
document.getElementById("download_button").onclick = function() {
var link = document.createElement("a");
link.download = "image.png";
canvas.toBlob(function(blob){
link.href = URL.createObjectURL(blob);
console.log(blob);
},'image/png');
console.log(link.href);
link.click();
}
console.log(blob)
in the callback function returns: Blob {size: 64452, type: "image/png"}
But console.log(link.href)
returns nothing.
Am I not using .createObjectURL
correctly?
I used to work with toDataURL
, but it stopped working above a certain canvas size. And this post canvas.toDataURL() download size limit suggested to try toBlob
.
Upvotes: 26
Views: 53916
Reputation: 17334
You might also create a custom prototype to get an equivalent to canvas.toDataURL()
HTMLCanvasElement.prototype.toObjectURL = async function(
mimeType = "image/jpeg",
quality = 0.85
) {
return new Promise((resolve, reject) => {
this.toBlob(
(blob) => {
if (!blob) {
reject("Error creating blob");
return;
}
const blobUrl = URL.createObjectURL(blob);
resolve(blobUrl);
},
mimeType,
quality
);
});
};
<canvas id="canvas" width="200" height="200"></canvas>
<img id="imgBlob" src="" alt="">
<img id="imgUrl" src="" alt="">
<p><a id="linkObjectUrl" href="" download="image.jpg">Download img (object URL)</a> | <a id="linkDataUrl" href="" download="image.webp">Download img (data URL)</a></p>
<script>
// example canvas
window.addEventListener('DOMContentLoaded', async(e) => {
// draw example on canvas
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
const img = new Image();
img.crossOrigin = "anonymous";
img.src = "https://picsum.photos/id/237/200/200";
await img.decode();
ctx.drawImage(img, 0, 0);
// get object URL
let quality = 0.5;
let objectUrl = await canvas.toObjectURL("image/jpeg", quality);
let dataUrl = canvas.toDataURL("image/webp", quality);
// set object URLs
linkObjectUrl.href = objectUrl;
linkDataUrl.href = dataUrl;
// apply to image elements
imgBlob.src = objectUrl;
imgUrl.src = dataUrl;
})
</script>
The method needs to be called in a async function.
let objectUrl = await canvas.toObjectURL("image/jpeg", quality);
Download links don't work in SO snippets.
See also codepen example
Upvotes: 1
Reputation: 3908
My solution to the problem:
async function getImage({
canvas,
width,
height,
mime = 'image/jpeg',
quality = 0.8,
}) {
return new Promise(resolve => {
const tmpCanvas = document.createElement('canvas');
tmpCanvas.width = width;
tmpCanvas.height = height;
const ctx = tmpCanvas.getContext('2d');
ctx.drawImage(
canvas,
0,
0,
canvas.width,
canvas.height,
0,
0,
width,
height,
);
tmpCanvas.toBlob(resolve, mime, quality);
});
}
const photo = await getImage({ canvas, width: 500, height: 500 });
Upvotes: 13
Reputation: 6924
Your code is fine.. just use it at the right time :)
canvas.toBlob(function(blob){
link.href = URL.createObjectURL(blob);
console.log(blob);
console.log(link.href); // this line should be here
},'image/png');
Upvotes: 31