How to make a collage using Javascript: Part 2 (Javascript section)
Tags: javascript, canvas, project
Published: 2021-02-07 16:15:58

**Note:** This is a part two, if you need the starting files check [part 1: starting files](https://dev.to/coulke/how-to-make-a-collage-using-javascript-part-1-starter-files-4a13) <br/>
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:

``` Javascript
"use strict";
const chooseLinks = document.querySelectorAll(".choose");
chooseLinks.forEach(function (link, index) {
    link.setAttribute("href", "upload.html?t=" + (index + 1));
});
```
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 <br/>
Add to`index.html` before closing the `body` tag: <br/>
``` Javascript
<script src="./index.js"></script>
<script src="./upload.js"></script>
```
That's all about the `index.js`, now create `upload.js` file and inside it we'll declare the following variables. <br/>
`upload.js`:
````javascript
"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");
```
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](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/getContext) for in-depth explanation on canvas context.
Here's the function for saving the images, add it to `upload.js` without the line numbers.
```javascript
/**
 * 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    }
  }
```
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](https://developer.mozilla.org/en-US/docs/Web/API/FileReader).
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:
```javascript
inputElement.addEventListener("change", saveImages);
```
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:
```Javascript
context.drawImage(image, dx, dy, dWidth, dHeight);
```
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`:
```javascript
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
  }
}
```
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.
```javascript
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
  }
}
```
Template 3 is as followed:
```javascript
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
  }
}
```
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`:
```javascript
previewButton.addEventListener("click", (e) => {
  e.preventDefault();
  preview.style.display = "block";
  previewCollage();
});
``` 
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:
```javascript
/**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 }
```
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:
```javascript
location.search
>"?t=2" //returns
```
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:
```javascript
['?t','1']
```
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](https://dev.to/coulke/how-to-make-a-collage-using-javascript-part-3-downloading-the-collage-1g4c).