Manipulating Images on Web Pages with CamanJS
A short time ago I was looking for an image manipulation library that I could use in a personal project. One library that I found and that appealed most to me was CamanJS, a JavaScript-based canvas manipulation library.
But wait… Since CSS supports basic image manipulation functionality out of the box you might be wondering why we’d want to use a JavaScript library for this. Well, besides browser support, Using CamanJS has a lot of advantages. It provides far more filters and options to manipulate images. You can create your own advanced filters and have control over every pixel in your image. You can use its built-in blend modes and layering system. And it enables you to manipulate cross-domain images and save the manipulated images.
Now, let’s start exploring the features that CamanJS offers!
Including Necessary Files
To start working with CamanJS simply include the library in your page. This minified CDN version that I am referencing here has all the plugins in addition to core functionality combined in a single file:
<script src="https://cdnjs.cloudflare.com/ajax/libs/camanjs/4.1.2/caman.full.min.js"></script>
The syntax for functions has changed a little from version 3 to 4 of CamanJS. so make sure you include a version greater than 4 when working following along with this tutorial.
Manipulating Images via HTML Attributes
CamanJS can be used to manipulate images using the data-caman
attribute. The code below demonstrates you how to apply a brightness filter of strength “10” and contrast of strength “30” to an image:
<img data-caman="brightness(10) contrast(30)"
src="yourimage.jpg">
Similar syntax is used for the other 18 filters that come prepackaged with the library. For example:
<img data-caman="love() hazyDays()"
src="yourimage.jpg">
Manipulating Images via JavaScript
Alternatively, you can write a few lines of JavaScript to manipulate an image. The result when using JavaScript is no different from using the data-caman
attributes.
Caman('#your-image-id', function () {
this.brightness(40);
this.contrast(-10);
this.sinCity();
this.render();
});
Implementing Controls for an Image Editor
Filters that don’t require much tweaking can be triggered on button click. Some filters like vintage()
, lomo()
, and sinCity()
do not require a parameter. Other filters like contrast()
and noise()
need an integer value as a parameter. This value decides the strength of the filters.
Complex filters like tiltShift()
, posterize()
, and vignette()
require more than one parameter. The code snippet below demonstrates how to work with three of these filters. Other filters can be coded likewise. Here is the HTML:
<canvas id="canvas"></canvas>
<button id="vintagebtn">Vintage</button>
<button id="noisebtn">Noise</button>
<button id="tiltshiftbtn">Tilt Shift</button>
Here is the JavaScript/jQuery to apply filters on button click:
var vintage = $('#vintagebtn');
var noise = $('#noisebtn');
var tiltshift = $('#tiltshiftbtn');
vintage.on('click', function(e) {
Caman('#canvas', img, function() {
this.vintage();
this.render();
});
});
noise.on('click', function(e) {
Caman('#canvas', img, function() {
this.noise(10);
this.render();
});
});
tiltshift.on('click', function(e) {
Caman('#canvas', img, function() {
this.tiltShift({
angle: 90,
focusWidth: 600
}).render();
});
});
tiltshift()
also accepts additional parameters like startRadius
and radiusFactor
. vignette()
takes two parameters size
and strength
. You can refer to the CamanJS documentation to gain a deeper understanding of all available filters.
Implementing Slider Controls
Filters like brightness, contrast, and hue that require comparatively more granular control over their values work perfectly with input range sliders. As you will see, implementation of slider controls differ slightly from button controls. You can use the following HTML to create range sliders:
<form id="silderInput">
<label for="hue">Hue</label>
<input id="hue" name="hue" type="range" min="0" max="300" value="0">
<label for="contrast">Contrast</label>
<input id="contrast" name="contrast" type="range" min="-20" max="20" value="0">
</form>
This block of jQuery handles all the manipulating:
$('input[type=range]').change(applyFilters);
function applyFilters() {
var hue = parseInt($('#hue').val());
var cntrst = parseInt($('#contrast').val());
Caman('#canvas', 'image.jpg', function() {
this.revert(false);
this.hue(hue);
this.contrast(cntrst);
this.render();
});
}
The applyFilters()
function is called whenever any of the input range sliders changes its value. This function stores the value of all range sliders in corresponding variables. These values are then passed as parameters to respective filters for editing the image.
I call this.revert(false)
every time I apply these filters so that the canvas reverts back to its original state. Using revert
makes sure that the filters are manipulating the original image and their effect is not compounding. The false
passed as a parameter avoids a momentary flash of the original image during the reverting process.
Another noteworthy detail is that I apply all the filters again even if just one of them changes in value. This is because users don’t expect the contrast to reset when they are adjusting the value of hue or brightness.
Creating Custom Filters in CamanJS
One cool feature among many others of this library is that you can extend it by creating your own filters and plugins. There are two methods to create custom filters. You can combine built-in filters with appropriate values or you can create your own filter from scratch.
Here is the jQuery to create your own filter:
Caman.Filter.register('oldpaper', function() {
this.pinhole();
this.noise(10);
this.orangePeel();
this.render();
});
To create filters from scratch, you’ll need to do some extra work, due to a few existing bugs that you can read about in this open issue on the GitHub repo.
Layers and Blend Modes
Besides filters, CamanJS comes with an advanced layering system. This gives you much more image manipulation power and options. Unlike Photoshop, the layers in CamanJS can be nested. It uses blend modes to apply layers to their parents. The default blend mode is normal
. CamanJS has ten blend modes in total, including common ones like multiply
, exclusion
, and overlay
.
Below is the jQuery to create a custom filter using layers and blend modes:
Caman.Filter.register('greenTint', function() {
this.brightness(-10);
this.newLayer(function() {
this.setBlendingMode("overlay");
this.opacity(100);
this.fillColor('#689900');
this.filter.brightness(15);
this.filter.contrast(10);
});
this.render();
});
Filters can be applied to both the original layer and new layer. Additionally, you can set several other properties like opacity and blend mode for the new layer. I have filled this layer with a solid color but you can fill it with another image by calling this.overlayImage('image.jpg')
.
Manipulating Cross-origin Images
If you need to manipulate images hosted on a different domain you can use the PHP proxy that comes with CamanJS. To enable this feature, you need to host this PHP script on your server. This script will proxy the image data from the remote source to your browser in order to circumvent the editing restrictions. Then you need to add following line to your JavaScript:
Caman.remoteProxy = Caman.IO.useProxy('php');
Saving the Edited Image
CamanJS has a built-in mechanism to save images after editing. With the current implementation, a call to this.save(png)
will open a download file prompt but you will have to rename the file and add a png
or jpg
extension. This is because on calling this function browsers are redirected to base64 encoding of the image and they don’t know the file type. The code snippet given below saves the image:
this.render(function () {
this.save('png');
});
Demo and Full Code
You can take a look at the demo of the image editor with all features in action in this CodePen demo, screen captured below:
As an exercise you may attempt to improve the user experience by creating some kind of indication that filters are currently working on the image or modify the save button in such a way that it no longer requires renaming of the file.
As we’ve seen, CamanJS is an incredibly useful image manipulation library with many filters and continuously expanding functionality, and I have barely scratched the surface of its features in this tutorial.