Generating image collages

Eugene Musebe

Introduction

A collage is a combination of multiple pictures combined into a single image. They are useful in various applications from presenting memories to summarising events. Instead of exploring multiple applications to generate collages, let us explore how we can build a simple Nuxt.Js app to generate them.

Codesandbox

The completed project is available on Codesandbox.

You can find the full codebase on my Github

Nuxt.Js setup

We will use NuxtJS for this tutorial. It is an intuitive Vue.Js framework designed for simplicity and productivity.

To set up our project we will use the create-nuxt-app utility. Ensure you have Yarn or NPM v5.2+ or v6.1+ installed and open your terminal in your preferred working directory.

1yarn create nuxt-app nuxtjs-image-collage
2# OR
3npx create-nuxt-app nuxtjs-image-collage
4# OR
5npm init nuxt-app nuxtjs-image-collage

The above will trigger a set of setup questions. Here are our recommended defaults:

You may now run the project on http://localhost:3000:

1cd nuxtjs-image-collage
2
3yarn dev
4# OR
5npm run dev

@nuxtjs/cloudinary setup

We will use Cloudinary to generate the image collage. It is a powerful media management platform with a comprehensive set of APIs and SDKs.

Let us install the recommended Nuxt.Js plugin: @nuxtjs/cloudinary.

1yarn add @nuxtjs/cloudinary
2# OR
3npm install @nuxtjs/cloudinary

Register the plugin to the modules section of our nuxt.config.js file:

1// nuxt.config.js
2export default {
3 ....
4 modules: [
5 '@nuxtjs/cloudinary'
6 ]
7 ...
8}

Once the above is done, let's configure our Cloudinary instance by adding a cloudinary section in our nuxt.config.js file:

1// nuxt.config.js
2export default {
3 ...
4 cloudinary: {
5 cloudName: process.env.NUXT_ENV_CLOUDINARY_CLOUD_NAME,
6 useComponent: true,
7 }
8}

The NUXT_ENV_CLOUDINARY_CLOUD_NAME variable we see above is an environmental variable. These are values we configure outside of our project code. Let us create our env file:

1touch .env

To obtain your Cloudinary cloud name, check the Account Details section on your account dashboard. If you don't have an account, you may register here.

Add your cloud name to your env file

1<!-- .env -->
2NUXT_ENV_CLOUDINARY_CLOUD_NAME=<your-cloud-name>

Setup the collage template

Before we start, we need to setup the template. Upload this template to the nuxtjs-image-collage/templates folder.

Selecting and uploading collage images

We want to allow our users to select the images they want to be displayed in the collage. Let us set up an HTML form to do this.

1<!-- pages/index.vue -->
2 ...
3 <form @submit.prevent="upload">
4 <div>
5 <label for="file"> Images </label>
6 <div class="mt-1">
7 <input @change="handleImagesChanged" id="file" name="file" type="file" accept="image/*" required multiple />
8 </div>
9 </div>
10
11 <div>
12 <div v-if="uploading" class="text-center">Please wait, upload in progress</div>
13 <button v-else type="submit" >
14 Upload & Generate Collage
15 </button>
16 </div>
17 </form>
18 ...

When a file is selected, we trigger the handleImagesChanged method. We also trigger the upload method when the form is submitted.

Let us add the JavaScript logic to support this.

1// pages/index.vue
2<script>
3export default {
4 data(){
5 return {
6 images:null,
7 uploading:false,
8 cloudinaryImages:[],
9 ...
10 }
11 },
12 methods:{
13 handleImagesChanged(e){
14 this.images = e.target.files;
15 },
16 readData : (f) =>
17 new Promise((resolve) => {
18 const reader = new FileReader();
19 reader.onloadend = () => resolve(reader.result);
20 reader.readAsDataURL(f);
21 }),
22 async upload(){
23 this.uploading = true;
24 for(let image of this.images){
25 const data = await this.readData(image);
26 const instance = await this.$cloudinary.upload(data, {
27 folder: "nuxtjs-image-collage",
28 uploadPreset: "default-preset",
29 });
30 this.cloudinaryImages.push(instance);
31 }
32 this.uploading = false;
33 },
34 ...
35 }
36}
37</script>

When the files are selected, we save them to state in the handleImagesChanged method. The upload method iterates through the images uploading them to the uxtjs-image-collage folder while storing the Cloudinary response instances.

The default-preset upload preset will be used for the uploads. Upload presets are rules we configure to be applied to each of our uploads. To create one, proceed to the add upload preset page. Here are our recommended defaults:

Name: default-preset Mode: unsigned Unique filename: true Delivery type: upload Access mode: public

Rendering the collage

Our app needs to understand which template we are using and the positions to place the images. For this, let us add the template and coordinates in our state.

1// pages/index.vue
2<script>
3export default {
4 data(){
5 return {
6 ...
7 template:'nuxtjs-image-collage/templates/Pink_Handdrawn_Heart_Polaroid_Photo_Collage',
8 templateCoordinates: [
9 {x:654, y:-20, label:'Center left'},
10 {x:-654, y:-20,description:'Center right'},
11 {x:0, y:510,description:'Bottom center'},
12 {x:0, y:-535,description:'Top center'},
13 {x:654, y:-535,description:'Top right'},
14 {x:-654, y:510,description:'Bottom left'},
15 {x:654, y:510,description:'Bottom right'},
16 {x:-654, y:-535,description:'Top left'},
17 ],
18 }
19 },
20 ...
21}
22</script>

We also need a method to get a random image from the Cloudinary image array and process the public_id for usage in our overlay transformation. This is by replacing all \ in the path with :. Let us add this method.

1// pages/index.vue
2<script>
3export default {
4 ...
5 methods:{
6 ...
7 getRandomImageUrl(){
8 const randomIndex = Math.floor(Math.random() * this.cloudinaryImages.length);
9 const randomImage = this.cloudinaryImages[randomIndex];
10 return randomImage.public_id.replace(new RegExp( '/','g'), ':');
11 },
12 }
13}
14</script>

With the above method and the above state, our app knows which images to overlay and where to overlay them in the template. Let us now add the final HTML to render the final collage.

1<!-- pages/index.vue -->
2<template>
3<div>
4 ...
5 <cld-image
6 v-if="!uploading && cloudinaryImages.length"
7 :public-id="template"
8 width="600"
9 height="680"
10 >
11 <cld-transformation
12 v-for="(coordinate,index) in templateCoordinates"
13 :key="index"
14 :overlay="getRandomImageUrl()"
15 width="574"
16 :y="coordinate.y"
17 :x="coordinate.x"
18 />
19 </cld-image>
20</div>
21</template>

Thus with the above code, the collage will only be displayed when uploading is complete and there are contents in the cloudinaryImages array. We display the template and in it overlay images at the coordinate locations.

On uploading images, a collage similar to this should be generated.

Conclusion

To learn more about how to apply Cloudinary transformations, feel free to refer to the image transformations documentation.

To implement the above at scale, you may also look to set the image overlay coordinates in the contextual metadata of the template and have multiple templates.

Eugene Musebe

Software Developer

I’m a full-stack software developer, content creator, and tech community builder based in Nairobi, Kenya. I am addicted to learning new technologies and loves working with like-minded people.