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.
- 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.
- Scroll to the Firebase SDk Snippet and select Config copy the content and paste elsewhere as we will need it later on.
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.
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.
- 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.
- 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:
- Allow Users Upload images with options that let them add a brand.
- 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