Image Branding in a Vuejs application using Imagekit.

Introduction

Photographs and Images are properties of an individual behind the camera or the hand controlling the brush. And being able to show case this artwork and wonderful shots are becoming much easier these days as digital galleries floods the internet.

This may seem like a trivial task but as times goes on, more time are spent on adding brand to the images created before they hit the internet galleries due to the problem of intellectual theft, which resulted in the artist branding his/her work. And using the ImageKit service, we can integrate into a Gallery Application a function that aids the owner of a photograph to properly brand and author his/her image without the use of complex image manipulation tools.

In this article we will be looking at how we can build a Branding Image using Vuejs and ImageKit.

Firstly what is Image Branding?

Image branding or image watermarking is the act of overlaying logo and text onto images to protect the copyright of images and photos , and also to curtail the illegal use of creative material without the permission of its creator. That being said we can now look into what ImageKit is all about.

Before we start let’s look at what Imagekit is and what it is used for.

Imagekit

ImageKit is an image CDN with an optimization, real-time transformation, and storage that you can integrate with existing setup in minutes.

Before further we are going to start by creating a Vuejs app.

Create a new Vuejs Application

In order to try out this new idea, we are going to create a new Vuejs application, for this project, and we are going to be installing Vuejs CLI if we don’t have it installed it our system.

Open your terminal and run the command below.

npm install -g @vue/cli
# OR
yarn global add @vue/cli

Once the installation is done, head over to project folder where this little project is going to be built from, fire up your terminal, ensure you have your computer connected to the internet and run the below command to create a Vuejs project.

vue create fotograf

Once you run that command by pressing Enter. You will be prompted with the following options:

Vue CLI v4.5.11
? Please pick a preset:
❯ Default ([Vue 2] babel, eslint)
  Default (Vue 3 Preview) ([Vue 3] babel, eslint)
  Manually select features

Select Vue 2 with ENTER and Vue CLI will start creating your app. When completed, cd (change directory) in your project name (fotograf):

 cd fotograf

Next, start the application with yarn serve. or npm run serve This will run your project at a port on your localhost, usually :8080. If it’s a different port, the CLI will tell you:

yarn serve
#or
npm run serve

You should be greeted with a Vue Welcome screen:

Getting Firebase up and Running

Some information from the author are needed to brand the images the end users are going to see, and we need to store this data somewhere we can retrieve it subsequently for use, so we will be making use of Firebase Firestore, though Imagekit Offers 2GB of free storage for storing images but for simplicity, we be taking advantage of Firebase storage.

Now head over to: Firebase Homepage

  • Create a new Project <fotograf>
  • Create a new Firestore database in Test Mode to grant access to the public.
  • Create a collection named fotografs with document fields, these are the data each image is going to be attributed to.

User-uploaded image: firebase-firestore-collection.png

  • Navigate to the Storage Page and create a new folder named: gallery_images
  • Select Rules Tab change the line with allow to the line below
allow read, write;
  • Then publish the settings
  • Navigate to the Project Settings > General Tab scroll down to Your apps section, select the Web Platform to create a new one. User-uploaded image: image.png
  • Scroll to the Firebase SDk Snippet and select Config copy the content and paste elsewhere as we will need it later on. User-uploaded image: image.png

Voila! we are done setting up Firebase Storage and Firestore, now let’s move to setting up ImageKit and how to add it to our project.

Getting an ImageKit Service Credentials

In order to use ImageKit we need to create an account, head over to ImageKit Registration Page to create one for this project. Once the project is created we get up to 2GB free space to store data.

User-uploaded image: image.png

Once that is done, we are going to link the Firebase Firestore storage with ImageKit. To do that we are going to follow the steps below.

  • Firstly to link Imagekit with Firebase storage, check in the External Storage Page, add a new origin with Origin Type: Web Server, then for the Base Url set it to: https://firebasestorage.googleapis.com, leave the advanced option unchecked then submit.

User-uploaded image: ImageKit-external-storage.png

User-uploaded image: ImageKit-external-storage-new-origin.png

  • Secondly navigate to Image Settings Page > Media library Tab: proceed to turn off the ENSURE UNIQUE IMAGE NAMES DURING UPLOAD option, then save. this will prevent ImageKit from automatically renaming the font files we are going to upload later on, then save.

User-uploaded image: ImageKit-image-settings.png

  • Lastly head over to the Developers Page copy the URL-endpoint and Public Key paste them elsewhere as they would be needed later on.

Once that is done, we can now return to our project directory to start structuring the application itself.

Installing Necessary Modules:

Now we are going to integrate ImageKit and Firebase into the Vuejs app that we created, so now run the imageKit Module for Vuejs and also Firebase tool. Open your terminal cd to the project folder of the application and run the below command.

yarn add imagekitio-vue firebase
# or
npm install imagekitio-vue firebase

When the installation is completed now open up your code editor in the root folder of the application, if you use VS Code Editor you could run the command below to start VS Code in that directory.

code .

Now firstly we’re going to initialize Firebase in the app create a new file firebaseConfig.js in the directory: src. Now we are going to import firebase and initialize it with the configuration data we got from Firebase Console so your file should be similar to this:

//import firebase module
import firebase from "firebase";
import "firebase/firestore";

//firebase config data object
const config = {
//replace this object content with the one you copied earlier on 
  apiKey: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  authDomain: "XXXXXXXXXXXX.firebaseapp.com",
  databaseURL: "https://XXXXXXXXXXX.firebaseio.com",
  projectId: "XXXXXXXXXXXXXXXXXXXX",
  storageBucket: "XXXXXXXXXXXXXX.appspot.com",
  messagingSenderId: "XXXXXXXXXXXXXX",
  appId: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
};

//initialize firebase with configuration
firebase.initializeApp(config);
//instantiate firebase firestore
export const db = firebase.firestore();
//instantiate firebase storag
export const storage = firebase.storage();
//instantiate firebase firestore fotografs collection
export const imageCollection = db.collection("fotografs");

Next step is to add Imagekit to the project, so locate the main.js file in the same folder as the above config file, add the import of the imagekitio-vue module after Vue to make it globally available:

...
//should be imported after the Vue module itself
import ImageKit from "imagekitio-vue"
//making it global
Vue.use(ImageKit, {
  urlEndpoint: "https://ik.imagekit.io/XXXXXXX", //  XXXXXX is your_imagekit_id
  publicKey: "XXXXXXXXXXXXXXXXX", // optional 
});
...

The above is the the URL-endpoint and the public key we retrieved from your ImageKit Developers Page. Now we are going to start scaffolding the Interface of the App right now let’s fasten our seat belt.

Scaffolding The Application:

Scaffolding the app, this app is going to have two primary services to offer to the users:

  1. Allow Users Upload images with options that let them add a brand.
  2. Show the Branded Images to the viewer.

Which is going to look close to this:

Let’s start by building the app from No.1

Image Uploading Form:

Create a new file(Vue Component) in the component directory to handle all the image uploading service. The data we need from the author would be as same we use in our Firebase Firestore model.

  • Author Name.
  • Author Initial.
  • Brand Color .
  • Brand Font.
  • Finally the Image to be branded.
./src/components/UploadForm.vue

<template>
  <div @click.self="closeForm" class="backdrop">
    <div class="panel shade grid">
      <form class="form mx-auto">
        <div>
          <input v-model="authorName" type="text"
            class="inputs authorNameInput" placeholder="John Doe"/>
          <input
            v-model="authorInitial" type="text" maxlength="3"
            minlength="3" class="inputs initialsInput" placeholder="JDK"/>
          <input
            v-model="brandColor" name="brandColor" type="color"
            class="inputs brandColorInput"/>
          <input
            @change="inputToState" ref="imgx" type="file" 
            accept=".png, .jpg, .jpeg"
            class="inputs imageInput"
          />
        </div>
        <div v-show="imageDataUrl" class="inputs imagePreview">
          <img :src="imageDataUrl" />
        </div>
          <select
            name="brandFont"
            class="inputs brandFontSelect"
            v-model="brandFont"
          >
            <option selected value="choice">Choice</option>
            <option value="rossela">Rossela</option>
            <option value="signatrue">Signatrue</option>
          </select>
          <div class="brandFontPreview" :style="setCurrentFont()">
            {{ authorInitial }} - {{ authorName }}
          </div>
          <button
            :disabled="validateForm() || loading"
            type="button"
            @click.prevent="uploadImage()"
            class="upload_btn"
          >
            Upload </button>
          <button type="cancel" @click.prevent="closeForm" class="cancel_btn">
            Cancel!
          </button>
      </form>
    </div>
  </div>
</template>
<script></script>
<style scoped src="@/styles/UploadForm.css">
/* import an external css file */
</style>

To allow the author add more styling options we are going to create a directory styles inside the src directory, and also create a new UploadForm.css file inside of it. We are also going to add some stylish fonts to make more impression, place the downloaded font files .ttf inside of a new directory fonts inside the src and then upload them to ImageKit Media Library Page.

Then import them for use in the css file we created earlier, you can create more styles to change the look of the form.

./src/styles/UploadForm.css

@font-face {
  src: url("../fonts/choice.ttf");
  font-family: choice;
}
@font-face {
  src: url("../fonts/rossela.ttf");
  font-family: rossela;
}
@font-face {
  src: url("../fonts/signatrue.ttf");
  font-family: signatrue;
}
...
//add your styles to change the look of the form

Now lets bind those lifeless input to state and make the come alive:

...
</template>
<script>
const fb = require("../firebaseConfig");
export default {
  data() {
    return {
      authorName: "",
      brandColor: "",
      authorInitial: "",
      brandFont: "choice",
      imageData: null,
      imageDataUrl: "",
      loading: false,
    };
  },
  methods: {}
}
</script>

Now lets go over this one after another:

Imported the initialized Firebase function to use in this component method later on.

const fb = require("../firebaseConfig");

Created a Vue state to hold the data the user chooses.

export default {
  data() {
  return {
    authorName: "",
    brandColor: "",
    authorInitial: "",
    brandFont: "choice",
    imageData: null,
    loading: false,
  };
},

Empty method to hold all the functions the form needs:

  • form validation inputs.
  • image uploading to Firebase, etc.
  methods: {}

Now lets start adding those functions, But first is validation function; all fields are required don’t want the form to be submitted with missing data, so we are going to perform a check before the upload button is enabled.

...
methods: {
...
methods: {
//check if all required field is filled
validateForm() {
  let {
    authorName,
    brandColor,
    authorInitial,
    brandFont,
    imageData,
  } = this;
  return authorName && brandColor && authorInitial && brandFont && imageData
    ? false
    : true;
},
}

Since Vue does not support 2 way data binding on Input with type=``"``file``", next is getting the selected image file from the input and saving it to state manually. Create a blob link to use for the image preview using window.URL.createObjectURL() function.

...
//save selected images to state
inputToState(x) {
  let files = x.target.files || x.dataTransfer.files;
  this.imageData = files[0];
},

Now lets add the miscellaneous we will need:

...
//set the font for the preview box
setCurrentFont() {
  return {
    fontFamily: this.brandFont,
    color: this.brandColor,
  };
},
//get the extenison for the image selected
getFileExtension(filename) {
  var ext = /^.+\.([^.]+)$/.exec(filename);
  return ext == null ? "" : ext[1];
},

Now it’s time to upload the data in the form to Firebase Firestore, this function takes care of that.

...
//upload the selected image and branding data to firebase
uploadImage() {
  this.loading = true;
  let img = this.imageData;
  let uploadTask = fb.storage
//generate a random image name and append extension
    .ref(
      `gallery-images/${(0 | (Math.random() * 9e6)).toString(
        36
      )}.${this.getFileExtension(img.name)}`
    )
    .put(img);
  uploadTask.on(
    `state_changed`,
    (snapshot) => {
    //you can calculate the upload progess here
    },
    (error) => {
      console.log(error.message);
      this.loading = false;
    },
    () => {
      uploadTask.snapshot.ref.getDownloadURL().then((url) => {
      //prepare the branding data to be uploaded
        let data = {
          authorName: this.authorName,
          authorInitial: this.authorInitial,
          brandFont: this.brandFont,
          brandColor: this.brandColor,
          imageUrl: url,
        };
        fb.imageCollection.add({ ...data }).then((doc) => {
          this.loading = false;
          setTimeout(() => {
          }, 2000);
        });
      });
    }
  );
},

And with that we are done with the uploaded form and we are going to dive into displaying the uploaded image.

Gallery Image View:

Locate the App.vue component in the src directory, clear all it’s content and replace it with the codes below to create a simple gallery frame.

./src/App.vue

<template>
  <div id="app">
    <div class="container">
    </div>
  </div>
</template>
<script>
export default {
  data() {
    return {
      resImageHeight: window.innerHeight * 0.6,
      resImageWidth: window.innerWidth,
      slideIndex: 0,
      formShow: false,
      gallery_images: [],
    };
  },
  created() {},
  components: {},
  methods: {},
};
</script>
<style src="@/styles/App.css"></style>

Next we import the Form component for use in this component:

...
<script>
//import our instantiated firebase module
const fb = require("../firebaseConfig");
//import our component for uploading images
import UploadForm from "./components/UploadForm";
...

Now lets loop through the available images and render them using Imagekit component and at the same time apply the transformation.

...
<div
  v-show="Object.keys(gallery_images).length > 0"
  :class="[
    'mySlidesContainer',
    x == slideIndex ? 'mySlidesActive' : 'mySlidesInactive',
  ]"
  v-for="(fbImage, x) in gallery_images"
  :key="fbImage.id"
>
  <div class="numbertext">{{ x + 1 }} / {{ gallery_images.length }}</div>
  <ik-image
    :path="fbImage.imageUrl"
    loading="lazy"
    :lqip="{ active: true }"
    :transformation="[
      {
        width: resImageWidth,
        height: resImageHeight,
      },
      {
        ot: titleCase(fbImage.authorName),
        ots: 24,
        overlayX: 5,
        overlayY: 'N15',
        otw: 250,
        oth: 50,
        or: '26',
        otf: fbImage.brandFont + '.ttf',
        otia: 'left',
        otc: fbImage.brandColor.substring(1).toUpperCase(),
        otp: '1_25_1_55',
      },
      {
        ot: fbImage.authorInitial,
        ots: 18,
        overlayX: 10,
        overlayY: 'N18',
        otc: 'FFFFFF',
        ott: 'b',
        otbg: fbImage.brandColor.substring(1).toUpperCase(),
        otf: fbImage.brandFont + '.ttf',
        or: 50,
        otw: 43,
        oth: 50,
        otp: '3_5',
      },
    ]"
  />
</div>
<a
  v-show="Object.keys(gallery_images).length > 0"
  class="prev"
  @click="prevSlide()"
  ></a
>
<a
  v-show="Object.keys(gallery_images).length > 0"
  class="next"
  @click="nextSlide()"
  ></a
>...

Here’s whats happening in the above code, what we did was loop though the list of image links available in the gallery_images and supply them to <ik-image``/> Component, applying a chain of valid ImageKit Transformation Properties based on the branding data of that image, so that the images that are returned to the gallery viewer has brand on it.

So let Highlight the transformations used above.

Path Prop: a path to the image URL you want ImageKit to render for you.

  :path="fbImage.imageUrl"

This prop and current value instructs ImageKit to Lazy Load the image.

  loading="lazy"

The options serves blurred placeholder of the image until the image is completely fetched from the server.

  :lqip="{ active: true }"

This is where we manipulate the images by adding transformations.

  :transformation="[]"

Height / Width or h / w: Here we ask ImageKit to return the image in the size specified.

      width: resImageWidth //value must be numeric
      height: resImageHeight //value must be numeric

Since we can chain transformations, we add an Overlay Text to the current image size. overlayText or ot: The text to show on the image is the Author name.

      ot: titleCase(fbImage.authorName),

overlayX: Position of the overlay along the X-axis.

      overlayX: 5,

overlayY: Position of the overlay along the Y-axis, adding N before a digit indicates negative values.

      overlayY: 'N15',

overlayTextFontFamily: Sets the font family to specified a list of inbuilt supported font that can be found here or you can supply the full path to the uploaded font in the media library.

      otf: fbImage.brandFont + '.ttf',

overlayTextColor: Sets the color of the overlay text and values must be in upper cased HEX format, without the leading # sign.

      otc: fbImage.brandColor.substring(1).toUpperCase(), //FFFFFF

overlayTextBackground: here you can specifiy the background color of the overlay, in same upper cased HEX format, without the leading # sign, also you can add and extra value to account for the alpha component(Transparency): 34EF33``80 , the 80 account for 80% transparency

      otbg: fbImage.brandColor.substring(1).toUpperCase(), //3FE4F4 or 34EF3380

More information on text transformation can be found on the ImageKit Documentation page. Finally to get the list of uploaded images

...
methods:{
...
//load the links to the uploaded images from firebase firestore
loadImages() {
      fb.imageCollection.onSnapshot((querySnapshot) => {
        this.gallery_images = [];
        querySnapshot.forEach((doc) => {
          let dtx = doc.data();
          let urlx = new URL(dtx.imageUrl);
          //push the results to local state
          this.gallery_images.push({
            id: doc.id,
            authorName: dtx.authorName,
            authorInitial: dtx.authorInitial,
            brandColor: dtx.brandColor,
            brandFont: dtx.brandFont,
            //remove the domain name from the url as imagekit does not need it
            imageUrl: urlx.pathname + urlx.search + urlx.hash,
          });
        });
      });
    },
... 

In the above code, we used the Firebase function imported at the top of the <script> tag to fetch the list of data saved on Firestore Database to enable us get the list of images uploaded, which goes through the documents inside our firebase collection and uses the data to populate the gallery_image state.

Now that we can get the uploaded images, let’s run the command below to ensure that our app is working.

yarn serve
# or
npm run serve

Concluding Note:

After going through the steps from the top all the way down, you should have a knowledgeable understanding of how Image Transformations work with ImageKit, without the use of a server and how it can be customized to serve our purpose in the project (Branding Images). With your newfound knowledge, you can be able to add features such as:

  • Allowing the author to choose a font size.
  • Combine images to make a picture collage.
  • Previewing the Resulting Image before upload.
  • Or letting the user upload their brand image to be used in place of text.

And the demo of the this entire application can can be found here, or view the complete source at Github