Imagine having the ability to build our very own image editor, one that is not just functional but also oozes with style and user-friendliness. In this article, we will unveil the secret recipe for creating a custom image editor that is as delightful to use as it is to build.
"Building an image editor? Isn't that too complex?" Well, fret not! We are about to prove that with a pinch of Vue.js, a dash of Xata, and a sprinkle of imagination, we can create an image editor that will leave users clicking and smiling.
This article will discuss building an image editing tool that allows users to upload, transform, and download images effortlessly. We will walk through the process of building an image editor step by step using Vue.js and Xata.
Xata is a Serverless Data platform designed to simplify how developers work with data. It combines the power of a traditional database with the ease of use found in SaaS (Software as a Service) spreadsheet applications.
Xata recently introduced a new feature - File Attachments. This feature lets developers integrate files, images, and documents directly into their database records. It eliminates the need for separate storage services and streamlines the process of managing structured data and binary objects like images, videos, and documents.
In essence, Xata's File Attachments revolutionize data app development by simplifying the architecture and reducing the number of services developers need to manage while enhancing the user experience and versatility of applications.
GitHub
Check out the complete source code here.
Netlify
We can access the live demo here.
Prerequisite
Understanding this article requires the following:
- Installation of Node.js
- Basic knowledge of JavaScript
- Creating a free account with Xata
Creating a Vue app
We can create a new Vue app using the npm create vue@latest
command.
Scaffolding the project would provide a list of options from which we can select the best fit for the project.
Styling
The CSS framework we will use in this project is Tachyons CSS, which we will install by running the command below in the terminal.
npm i tachyons
Afterward, we will make it globally available for usage in the project by adding the line below in our src/main.js
:
import 'tachyons/css/tachyons.css';
Creating a Xata Database
To set up a database in Xata, we will start by logging into our existing account or creating a new one. After accessing our account, we will navigate to the database section in the user dashboard. Here, we can create a new database. We will name this database image-transformation
for our specific use case since it aligns with the task at hand.
Once we create this database, the next step is to establish a table within that database and define the necessary records or fields we want the table to contain. In our project, we will name the table images
. Within the images table, we will create a record called image
with a type of file. This image record will be the focal point for all our manipulations.
The accompanying media below provides a visual guide to creating a record.
Setting up Xata in our Vue application
To incorporate Xata into our project, we will follow these installation steps:
Install Xata SDK
We will run the following command in the command line interface:
npx xata
Initialize Xata
Now, we can initialize Xata for use within the application with this command:
xata init
Configuration Options
Xata will present us with various configuration options to choose from. After making our selections, Xata will generate essential files, including .xatrc
and .env
.
Edit API Key
To ensure our Vue application can access the Xata environment, we will need to modify the Xata API key within the Vue app like so:
VITE_XATA_API_KEY="add your api key"
We can now modify the xata.js
file to use this intialised VITE_XATA_API_KEY
like so:
const defaultOptions = {
enableBrowser: true,
apiKey: import.meta.env.VITE_XATA_API_KEY,
databaseURL:
"https://...",
};
We would have seamlessly integrated Xata into our project by following the steps above.
Creating the landing page
In an ideal scenario, our solution should include a welcoming landing page that provides users with essential information about the product. For our project, we will design a simple landing page that offers users a quick overview of the product's purpose. To achieve this, we will create a views/HomeView.vue
file and insert the following code:
<template>
<header class="vh-100 bg-light-pink dt w-100 helvetica">
<div
style="
background: url(http://mrmrs.github.io/photos/display.jpg) no-repeat
center right;
background-size: cover;
"
class="dtc cover ph3 ph4-m ph5-l tc">
<div class="tc mt4 mb5">
<h1 class="f2 f-subheadline-l measure lh-title nb1 fw9">X-Transformr</h1>
<h2 class="f6 fw6 black">
Store, transform and update images using Xata (File Attachments)
</h2>
</div>
</div>
</header>
</template>
At the addition of the code above, our view should appear like the below:
Adding the uploading mechanism
Creating the upload UI
We will include a user-friendly input interface that enables users to upload images from which we can extract text. We will integrate the following code block into the components/Uploadcontainer.vue
file to achieve this.
<template>
<div class="flex flex-column flex-row-ns pa3 calisto bg-black-05">
<div class="w-50-ns w-100 ph3">
<form enctype="multipart/form-data">
<h2>Add Image</h2>
<div
class="b--dashed bw1 b--light-purple pa3 hover-bg-black-10 bg-animate pointer relative h4">
<input
type="file"
id="fileInput"
accept="image/*"
class="input-file absolute w-100 h4 pointer o-0" />
<p class="tc f4">
Drag your image here to begin<br />
or click to browse
</p>
</div>
</form>
</div>
</div>
</template>
We achieved the following from the above:
- Implemented a
<form>
element with theenctype="multipart/form-data"
attribute, facilitating file uploads. - Incorporated an
input
field with thefile
type attribute, allowing file uploads and restricting the input to accept only image files using the**accept="image/*"
attribute."
Creating the transformation UI
Xata has a number of image transformation options that we can apply to images to transform them to our desired taste in real-time. In our case, we will focus on four transformation options, which include the height, width blur, and sharpness of the image. To achieve this, we want to create a mechanism that allows users to add the transformation option values. For this, in the components/Uploadcontainer.vue
we will create a <form></form>
and add input methods that can be fed to Xata.
In the code above, we achieved the following:
- Created a form with input fields that allow the user to adjust the parameters of interest
- In the data property, we initialized the input fields and set default values for each parameter.
- Added a download button to allow users to get the transformed images
In the views/HomeView.vue
, we imported the component like so:
<template>
<header class="vh-100 bg-light-pink dt w-100 helvetica">
<div
style="
background: url(http://mrmrs.github.io/photos/display.jpg) no-repeat
center right;
background-size: cover;
"
class="dtc cover ph3 ph4-m ph5-l tc">
<div class="tc mt4 mb5">
...
</div>
<Uploadcontainer />
</div>
</header>
</template>
<script>
import Uploadcontainer from "@/components/Uploadcontainer.vue";
export default {
components: {
Uploadcontainer,
},
};
</script>
We did the above to facilitate the component to show up on the landing page. At this point, our landing page should look like the below:
Adding an upload service
To handle the file upload to be displayed in the UI, we will create a utils/file-upload.service.js
and add the code block below:
function upload(formData) {
const photos = formData.getAll('photos');
const promises = photos.map((x) => getImage(x)
.then(img => ({
id: img,
originalName: x.name,
fileName: x.name,
url: img
})));
return Promise.all(promises);
}
function getImage(file) {
return new Promise((resolve, reject) => {
const fReader = new FileReader();
const img = document.createElement('img');
fReader.onload = () => {
img.src = fReader.result;
resolve(getBase64Image(img));
}
fReader.readAsDataURL(file);
})
}
function getBase64Image(img) {
const canvas = document.createElement('canvas');
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);
const dataURL = img.src;
return dataURL;
}
export { upload }
The code above reads the image we will upload, draws it into a canvas, and then converts it to a Base64 string.
Uploading the image to Xata
Before we can transform the image, we will have to first upload the image to Xata. To do this, we will edit the components/Uploadcontainer.vue
to the below:
The code block above does the following:
- Represented various component states during image processing using
STATUS_INITIAL
,STATUS_SAVING
,STATUS_SUCCESS
, andSTATUS_FAILED
definitions, which we initialised in the corresponding computed properties -isInitial
,isSaving
,isSuccess
, andisFailed
. -
Image Upload Section:
- This section contains a form (
<form>
) withenctype="multipart/form-data"
for file upload. - It displays an "Add Image" heading and a drag-and-drop or file input field that allows users to select image files.
- Depending on the component's state, it shows messages like "Drag your image here to begin" or "Adding image..." to provide user feedback.
- When an image is successfully added, it displays a success message along with an option to upload another image and previews the uploaded image.
- This section contains a form (
-
Component Data:
- The component manages various data properties, including
uploadedImages
,uploadError
,currentStatus
,uploadFieldName
,imageDataArray
,recordId
,imageURL
, andimageName
, to track the state and information related to image uploads and processing.
- The component manages various data properties, including
- Methods:
-
reset()
: Resets the form and component's state to its initial state -STATUS_INITIAL
-
save(formData)
: Initiate the imageuploadImageToXata()
to foster uploading to Xata by using theupload
service -
filesChange(fieldName, fileList)
: Handles changes in the selected image files and prepares aformData
object for upload. -
prepareFormData()
: Processes uploaded image data to create an object with metadata, base64 content, name, and URL for the uploaded image. -
uploadImageToXata()
: Uploads the processed image data to a database using the Xata client.
-
At this point, if you check your Xata dashboard, you should see an image uploaded like the below:
In this image, we can see the images
table with the image
record. This image
record currently has the uploaded image with the id
(record_id) which we will be using later in our project.
When we upload an image, we should see our UI look like this:
Transforming images
Now that we have successfully uploaded our image, it is now time to apply transformations. To do this, we will edit the components/Uploadcontainer.vue
file and add the following:
In this code block, we achieved the following:
- Added a conditional render (
v-if="imageURL"
) based on the existence of animageURL
property. If the image has been uploaded successfully to Xata and we have a URL, the form component for customising the transformation values will become visible to the user -
debounceTransform
: This function is a debounced version of theeditImage
method, which means it waits for a specified delay (2000 milliseconds or 2 seconds) after the user stops typing before executingeditImage
. -
editImage
: This asynchronous function performs image editing and transformation using data from the Xata API. It calculates a new image URL based on the user's height, width, sharpening, and blur input and updates the component's properties accordingly. - Installed lodash and used the
debounce()
function to control the rate at which thedebounceTransform
method is executed.
Download image
After editing the pictures, we made available functionality for the user to download the edited image. We will edit the components/Uploadcontainer.vue
to reflect this change.
<template>
<div class="flex flex-column flex-row-ns pa3 calisto bg-black-05">
<div class="w-50-ns w-100 ph3">
<!-- upload image -->
</div>
<section class="w-40-l w-50-m w-100 ph3 tl" v-if="imageURL">
<h2>Transform Image</h2>
<form class="ba b--black bw2 bg-white br2 mw6 center pv2 ph4 shadow-5 f6">
<!-- input fields -->
<button
@click="downloadimage"
class="center db pv2 ph3 mb3 tracked bg-black ba br3 white hover-black hover-bg-black-30 bg-animate pointer f7">
Download
</button>
</form>
</section>
</div>
</template>
<script>
import { transformImage } from "@xata.io/client";
export default {
data() {
return {
imageURL: "",
imageName: "",
transformedImageUrl: "",
};
},
computed: {...},
methods: {
downloadimage() {
this.transformedImageUrl = transformImage(this.imageURL, {
download: this.imageName,
format: "jpeg",
});
window.open(this.transformedImageUrl, "_blank");
},
},
mounted() {...},
};
</script>
At this point, our whole application should look like the below:
Conclusion
In this article, we talked about building an image editor using Xata. Xata acts as our database and the image transformer using the options made available by the Xata service.
Here are some additional resources that will be helpful when working with Xata:
Top comments (0)