A while back, I wrote some tutorials which described how to apply different kinds of filters and blend modes to an image using just CSS. This could be very helpful in situations where you want to show the grayscale, blurred, or high-contrast version of the same image. Instead of creating four different images, you could just apply these effects to the original image using a few lines of CSS.
Using CSS filters and blend modes works nicely in most cases. However, CSS doesn't modify the pixels of the image itself. In other words, the filters and blend modes or any other effects are not permanent.
If someone downloads an image with CSS filters applied to it, they will get the original image and not the modified version. This can be a major setback if you were planning on creating an image editor for your users.
If you want the image modifications to be permanent and allow the user to download the modified image, you can use HTML5 canvas. The canvas
element allows you to do a lot of things, including drawing lines and shapes, writing text, and rendering animations.
In this tutorial, we will focus on editing images loaded on the canvas. CSS3 already has built-in functionality to allow you to apply effects like contrast, brightness, and blurring directly. When working with HTML5 canvas, we will use a canvas manipulation library called CamanJS to edit the images.
The library supports basic effects like brightness, contrast, and saturation out of the box. This will save time and allow us to create more sophisticated filters based on these basic ones.
CamanJS Basics
The name of this library is based on the fact that it is used for doing (ca)nvas (man)ipulation in JavaScript(JS). Before you can start using different features of the library, you will have to include it in your project. This can be done either by downloading the library and hosting it yourself or by linking directly to a CDN.
There are two ways to use the library. The first option is to use the data-caman
attribute with your image elements. This attribute can accept a combination of different CamanJS filters as its value. For example, if you want to increase the brightness of an image by 20 and the contrast by 10, you can use the following HTML:
<img src="path/to/image.jpg" data-caman="brightness(20) contrast(10)">
Similarly, you can apply other filters like saturation, exposure, noise, sepia, etc. Besides the basic filters, CamanJS also gives you access to some more sophisticated filters out of the box. These filters can be applied to an image in a similar manner. To apply the sunrise
filter, you can simply use the following HTML:
<img src="path/to/image.jpg" data-caman="sunrise()">
Your second option for manipulating images is by calling Caman()
with the id
of the canvas where you have rendered the image and different filters that you want to apply to the rendered image.
Caman('#canvas-id', function () { this.brightness(20); this.contrast(10); this.render(); });
In this series, we will be going the JavaScript way to create our image editor.
Implementing Upload and Download Functionality
You need to provide users with a way to upload the images they want to edit so that you can render them on the canvas for further manipulation. Once the users have made the changes, they should also be able to download the edited images. In this section, we will add these two functions to our image editor.
Let's begin with the HTML needed to add the canvas and upload/download buttons:
<div class="preview-wrapper"> <canvas id="canvas"></canvas> <p class="process-message"></p> </div> <div class="editor-buttons"> <input type="file" id="upload-file" placeholder="Upload a Picture" /> <label for="upload-file">Upload a Picture</label> <button id="download-btn">Download Image</button> <br/> </div>
Here is the code for implementing the basic image upload functionality:
var img = new Image(); var canvas = document.getElementById('canvas'); var ctx = canvas.getContext('2d'); var fileName = ''; $("#upload-file").on("change", function(){ var file = document.querySelector('#upload-file').files[0]; var reader = new FileReader(); if (file) { fileName = file.name; reader.readAsDataURL(file); } reader.addEventListener("load", function () { img = new Image(); img.src = reader.result; img.onload = function () { canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0, img.width, img.height); $("#canvas").removeAttr("data-caman-id"); } }, false); });
We begin by creating some variables to store the name of the image file selected by the user and the context for our canvas. After that, we write the code to get the image file from the file input
after its change
event is fired. The files selected by a user are stored in a FileList
, and we can get the first file from the list using .files[0]
.
Once we have the file, we use a FileReader
object to read the contents of the file selected by the user. The onload
event for the FileReader
is triggered after the selected file has been read successfully.
Inside the onload
event handler for the FileReader
object, we create an HTMLImageElement
instance using the Image()
constructor. The src
attribute of the image is then set to the value of the result property of our FileReader
.
Once the image has loaded successfully, we set the width and height of our canvas to be equal to the width
and height
of the image selected by the user. After that, we draw the image on the canvas and remove the data-caman-id
attribute from the canvas.
The attribute is added automatically by CamanJS when setting up the canvas for editing an image. We need to remove it every time a user selects a new file in order to avoid any mixup between the old image file we were editing and the new file selected by the user.
Besides loading the image file in the canvas, we have also set the value of the fileName
variable to be equal to the name of the file selected by the user. This will be useful when we are saving the edited image.
Users will now be able to upload different images in your image editor. Once they have edited the image, they would also like to download them. Let's write some code that will allow users to save the edited image file.
$('#download-btn').on('click', function (e) { var fileExtension = fileName.slice(-4); if (fileExtension == '.jpg' || fileExtension == '.png') { var actualName = fileName.substring(0, fileName.length - 4); } download(canvas, actualName + '-edited.jpg'); }); function download(canvas, filename) { var e; var lnk = document.createElement('a'); lnk.download = filename; lnk.href = canvas.toDataURL("image/jpeg", 0.8); if (document.createEvent) { e = document.createEvent("MouseEvents"); e.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); lnk.dispatchEvent(e); } else if (lnk.fireEvent) { lnk.fireEvent("onclick"); } }
We use the jQuery .on()
method to execute a piece of code every time the click
event is fired for the download button. This code removes the file extension from the name of the image file selected by the user and replaces it with the suffix -edited.jpg
. This name is then passed to the download
function along with a reference to the canvas where we rendered and edited the image.
The download function creates a link and sets its download
attribute to filename
. The href
attribute contains the data URI for the edited image. After setting the value of these two attributes, we programmatically fire the click event for our newly created link. This click starts the download of the edited image.
Applying Built-in Filters
As I mentioned in the beginning of the tutorial, CamanJS comes with basic built-in filters. So you can directly apply brightness, contrast, sepia, saturation, exposure, noise, sharpen, vibrance, and hue. Some filters like brightness and contrast can have a negative as well as a positive value.
You can make the values as high or as low as you want, but a sensible choice would be to keep them between -100 and 100. For example, the image becomes white when you set the brightness to 100. So any value above 100 will be useless. Similarly, the image will become completely gray if you set the value of the contrast to -100.
Other filters like sepia, noise, sharpen, and blur will only accept a positive value. Keep in mind that the hue filter covers the full 360-degree circle, with values ranging from 0 to 100. The image will look exactly the same when you set the hue to 0 or 100.
Here is the HTML to add buttons for our image editor:
<div class="filter-buttons"> <div class="filter-group"> <button id="brightness-dec">-</button> <span class="filter-name">Brightness</span> <button id="brightness-inc">+</button> </div> <!-- More Such Buttons --> <div class="filter-group"> <button id="gamma-dec" class="disabled">-</button> <span class="filter-name">Gamma</span> <button id="gamma-inc">+</button> </div> </div> <div class="editor-buttons"> <input type="file" id="upload-file" placeholder="Upload a Picture" /> <label for="upload-file">Upload a Picture</label> <button id="download-btn">Download Image</button> <br/> <button id="vintage-btn">Vintage</button> <!-- More Such Buttons --> <button id="lomo-btn">Lomo</button> </div>
All the filters like brightness and contrast have been given increase and decrease buttons. However, the decrease button has been disabled for some filters like noise because they can't have a meaningful negative value.
We will apply the respective filters based on the button clicked with the help of the following JavaScript.
/* Similar Code for All Other Buttons */ $('#brightness-inc').on('click', function (e) { Caman('#canvas', img, function () { this.brightness(10).render(); }); }); $('#brightness-dec').on('click', function (e) { Caman('#canvas', img, function () { this.brightness(-10).render(); }); }); /* Similar Code for All Inbuilt Filters */ $('#nostalgia-btn').on('click', function (e) { Caman('#canvas', img, function () { this.nostalgia().render(); }); }); $('#majestic-btn').on('click', function (e) { Caman('#canvas', img, function () { this.herMajesty().render(); }); });
For the increase and decrease buttons, the strength of the filter is based on how its effect scales. For example, the brightness and contrast are increased by 10, but the value of gamma is only increased by 0.1 after each click.
The following CodePen demo shows the CamanJS image editor we created in action.
Some filters might take some time before you see their final outcome. In such cases, users might think that the filter is not working. We will be using events to keep readers updated about the progress of a filter. All this will be discussed in the next tutorial.
Final Thoughts
The first tutorial was meant to teach you how to create an image editor with basic image upload and download functionality which users can use to edit images. We used basic filters like noise, contrast, and brightness as well as some more complicated effects like Vintage and Nostalgia, which are built into the library.
In the next tutorial, you will learn about more CamanJS features like layers, blend modes, and events. And in the meantime, don't forget to review what we have available in the Envato Market for purchase, study, use, and review.
by Monty Shokeen via Envato Tuts+ Code
Attractive, post. I just stumbled upon your weblog and wanted to say that I have liked browsing your blog posts. After all, I will surely subscribe to your feed, and I hope you will write again soon! οικιακά φίλτρα νερού
ReplyDeletespinal traction devices I like your post. It is good to see you verbalize from the heart and clarity on this important subject can be easily observed...
ReplyDelete