DEV Community

Luteya Coulston
Luteya Coulston

Posted on

How to make a collage using Javascript: Part 3 (Downloading the collage)

Read first two parts here:

First let's define a function for downloading the image:

/**
 * Generate image from the canvas then downloads it.
 */
1 function downloadImage() {
2    const dataSrc = cvns.toDataURL("image/png");
3    let hiddenLink = document.querySelector("#hidden-link");
4    hiddenLink.setAttribute("href", dataSrc);
5    hiddenLink.setAttribute(
6      "download",
7      `collage${new Date().getMilliseconds()}`
8    );
9    hiddenLink.click();
10  }
Enter fullscreen mode Exit fullscreen mode

HTMLCanvasElement.toDataURL() as explained by MDN,

returns returns a data URI containing a representation of the image in the format specified by the type parameter.

On line 2 we add the type as image/png even though that's the default, you can add even as jpg.
On line 4 we add the href to the hidden link and put the dataSrc as the url. We also add the attribute of download and a name which starts with collage.
The hidden link is in the index.html file.
Still that won't work as we need to paint the main canvas but first we need to prevent the default of download button so it won't redirect.
Add to upload.js

downloadButton.addEventListener("click", function (e) {
    e.preventDefault();
    paintThenDownload(downloadImage);
  });
Enter fullscreen mode Exit fullscreen mode

We then define the paintThenDownload button.
Add to upload.js:

 /**
 * Paints the canvas then call the function to download image.
 * @param callback Function
 */
function paintThenDownload(callback) {
    let img1 = document.querySelector("#img-1");
    let img2 = document.querySelector("#img-2");
    let query = window.location.search;
    let pickedTemplate = query.split("=")[1];

    if (img1 !== null && img2 !== null) {
      if (pickedTemplate === "1") {
        template_1(img1, img2, "main");
      } else if (pickedTemplate === "2") {
        template_2(img1, img2, "main");
      } else if (pickedTemplate === "3") {
        template_3(img1, img2, "main");
      } else {
        template_1(img1, img2, "main");
      }
    }
    callback();
  }
Enter fullscreen mode Exit fullscreen mode

It's the same as the previewCollage function on the last section but this one receives a callback function and the templates third parameter is main and not preview.
The function is called and passed to the downloadImage function when the downloadButton is clicked.
That's it, I hope you found that worth your time.

Top comments (1)

Collapse
 
bellsal_b44bf6d profile image
BellSal

Useful series. One caveat worth adding to this part, because it cost me a long time to find: the hidden-link-plus-data-URL approach works everywhere on desktop and silently does nothing on phones. An Android WebView has no download manager attached unless the host app wires one up, so the navigation is dropped; iOS Safari refuses the download attribute on data: URLs from a synthetic click. No exception, no console warning, no file — downloadImage() just returns, and the user assumes they tapped wrong.
On iOS the route that does work is the Web Share API with a File, but it has to stay inside the user gesture, so the base64-to-File conversion must be synchronous. Do it after an await — fetch(dataUrl).then(r => r.blob()), or canvas.toBlob — and the activation is already gone, so the share sheet is dismissed without a word. Same silent failure, different cause.