DEV Community

Luteya Coulston
Luteya Coulston

Posted on

How to make a collage using Javascript: Part 2 (Javascript section)

Note: This is a part two, if you need the starting files check part 1: starting files

If you were keen on the index.html files on the last section, the template links did not have a href attribute. That's because I wanted to do it dynamically, so create an index.js file and paste the following code:

"use strict";
const chooseLinks = document.querySelectorAll(".choose");
chooseLinks.forEach(function (link, index) {
    link.setAttribute("href", "upload.html?t=" + (index + 1));
});
Enter fullscreen mode Exit fullscreen mode

The links had a class of choose, the above code stores them in an htmlcollection which can be traversed using a loop. In this case I use a forEach loop that takes in the parameters of link and index. Note that an htmlcollection is just like an array which starts at 0 so in setting the attribute, we add 1 to the index as there is no template 0.
The ?t= part is the query parameter which look at later.

Inside index.html, add link to the upload.js file

Add toindex.html before closing the body tag:

<script src="./index.js"></script>
<script src="./upload.js"></script>
Enter fullscreen mode Exit fullscreen mode

That's all about the index.js, now create upload.js file and inside it we'll declare the following variables.

upload.js:

"use strict";
/**Maximum number of files to upload */
const maxFiles = 2;
const inputElement = document.querySelector("#files");
const downloadButton = document.querySelector("#downloadButton");
const previewButton = document.querySelector("#previewButton");
const hiddenImages = document.querySelector("#hidden-images");
/**main canvas */
const cvns = document.querySelector("#cvns");
/**preview canvas */
const preview = document.querySelector("#preview");
/**main canvas context */
const ctx = cvns.getContext("2d");
/**preview canvas context */
const previewCtx = preview.getContext("2d");
Enter fullscreen mode Exit fullscreen mode

Since we're not using any database, we need a place to store our uploaded images, localStorage is good but some images can be bigger than the localStorage limit. Now that's why there's a declared hiddenImages variables, when uploading the images they will appended inside the hiddenImages element. If you check styles.css #hidden-images has a display of none.
Also, before painting the canvas, we need a context which for in case is 2d. You can refer to MDN for in-depth explanation on canvas context.
Here's the function for saving the images, add it to upload.js without the line numbers.

/**
 * Saves the uploaded image in the dom.
 */
1 function saveImages() {
2    let uploadedFile = inputElement.files;
3    for (let index = 0; index < maxFiles; index++) {
4      if (uploadedFile[index]) {
5        const reader = new FileReader();
6        reader.readAsDataURL(uploadedFile[index]);
7        reader.onloadend = function () {
8          let src = this.result;
9          let img = document.createElement("img");
10          img.setAttribute("src", src.toString());
11         img.setAttribute("id", `img-${index + 1}`);
12          hiddenImages.append(img);
13        };
14      }
15    }
  }
Enter fullscreen mode Exit fullscreen mode

The input element for uploading the images was stored inside the inputElement variable. Input form element of type file have a property of files which we have stored in uploadedFile variable on line 2 then loop through it twice to access the two images that will be uploaded.
On line 5 we store a FileReader object inside the reader variable. FileReader is an object that enables that can read file data just as it's name says. Check documentation here.
FileReader has some event handlers and one one of them is the onloadend which is triggered after file is read.
On line 7 we define the function to be triggred. The FileReader will return a a result and we store it inside the local variable src. Then on line 9 we dynamicaly create an image element and set it's source and id on line 10 - 11. Each image will now have an id of img-1 and img-2 respectively.
Our function is now finished but we need to call it when images are uploaded, how do we do that? It easy look:

inputElement.addEventListener("change", saveImages);
Enter fullscreen mode Exit fullscreen mode

Whenever our input changes, it will be called.
The next thing we need to do is painting on the canvas but before that let me highlight on how the canvas draws images.
The following snippet is a sample of how it works:

context.drawImage(image, dx, dy, dWidth, dHeight);
Enter fullscreen mode Exit fullscreen mode

The dx and dy parameters are x and y axis while the dWidth and dHeight are the image width and height respectively.
NOTE:The canvas x and y axis starts at the top-left corner.
Let's draw template 1..
Add inside upload.js:

function template_1(img1, img2, show) {
  if (show.toLowerCase() === "preview") {
    /**preview canvas */
    previewCtx.drawImage(img1, 0, 0, 275, 275); //first image
    previewCtx.drawImage(img2, 100, 100, 137.5, 137.5); //second image
  } else if (show.toLowerCase() === "main") {
    /**main canvas */
    ctx.drawImage(img1, 0, 0, 550, 550); //first image
    ctx.drawImage(img2, 200, 200, 275, 275); //second image
  }
}
Enter fullscreen mode Exit fullscreen mode

It takes three parameters, two images and a show parameter. If show is defined as preview then it draws on the smaller canvas and if show is main it draws on the bigger canvas. Note the context used are the ones we defined as previewCtx and ctx at the start.
Template 2 is also the same just different x and y axis.

function template_2(img1, img2, show) {
  if (show.toLowerCase() === "preview") {
    /**preview canvas */
    previewCtx.drawImage(img1, 0, 0, 137.5, 275); //first image
    previewCtx.drawImage(img2, 137.5, 0, 137.5, 275); //second image
  } else if (show.toLowerCase() === "main") {
    /**main canvas */
    ctx.drawImage(img1, 0, 0, 275, 550); //first image
    ctx.drawImage(img2, 275, 0, 275, 550); //second image
  }
}
Enter fullscreen mode Exit fullscreen mode

Template 3 is as followed:

function template_3(img1, img2, show) {
  if (show.toLowerCase() === "preview") {
    /**preview canvas */
    previewCtx.drawImage(img1, 0, 0, 275, 137.5); //first image
    previewCtx.drawImage(img2, 0, 137.5, 275, 137.5); //second image
  } else if (show.toLowerCase() === "main") {
    /**main canvas */
    ctx.drawImage(img1, 0, 0, 550, 275); //first image
    ctx.drawImage(img2, 0, 275, 550, 275); //second image
  }
}
Enter fullscreen mode Exit fullscreen mode

Let's write a function of previewing the collage and then on the next last chapter we'll see how to download from the canvas.
First we'll prevent the previewButton from redirect as that is the default behaviour of links.
Add to upload.js:

previewButton.addEventListener("click", (e) => {
  e.preventDefault();
  preview.style.display = "block";
  previewCollage();
});
Enter fullscreen mode Exit fullscreen mode

The preview canvas was also hidden, on clicking as you can see above we display it then we call the previewCollage function which we define next.
Add to upload.js without the line numbers:

/**paints the preview canvas. */
1 function previewCollage() {
2  let img1 = document.querySelector("#img-1");
3  let img2 = document.querySelector("#img-2");
4  let query = window.location.search;
5  let pickedTemplate = query.split("=")[1];
6
7  if (img1 !== null && img2 !== null) {
8    if (pickedTemplate === "1") {
9      template_1(img1, img2, "preview");
10    } else if (pickedTemplate === "2") {
11      template_2(img1, img2, "preview");
12    } else if (pickedTemplate === "3") {
13      template_3(img1, img2, "preview");
14    } else {
15      template_1(img1, img2, "preview");
16    }
17  }
18 }
Enter fullscreen mode Exit fullscreen mode

Remember at the start when we dynamically added links to the chooseLinks variable inside the index.js file. Now the links had a url path like upload.html?t=index + 1. The ?t part as I said is the query parameter/search. To get it you can try on the console to write:

location.search
>"?t=2" //returns
Enter fullscreen mode Exit fullscreen mode

To get the number we use split which will return an array as on line 5 . Since we used '=' to split, the array returned will be like:

['?t','1']
Enter fullscreen mode Exit fullscreen mode

Now the template picked can be found on index 1 as arrays start at index 0.
If it doesn't make sense try and re-read or as me in the comments I'll reply. Next section is downloading the collage.

Discussion (0)