I've made an image filtering client for JavaScript that runs on jQuery, Vex and Fabric.JS.
You simply include the required files in your html page, along with something like:
<script type="text/javascript">
ImageFilter.defaultOptions.imageCallback = server.upload();
</script>
and then have a <a><img /></a>
structure where you add an empty data attribute (data-imagefilter
) and a filled data attribute (data-imagefilter-image
, which is filled with the src).
Once you click on the image, it opens up a popup and lets you edit and save, or edit and cancel a bunch of filters. This has issues running on the local machines are that cross-origin domains don't work, however, this is intended for use on a live environment.
/* ImageFilter.JS
*
* Dependencies:
* - Browser Environment
* - Fabric.js - fabricjs.com
* - jQuery
* - Vex - github.hubspot.com/vex
*/
(function(global) {
"use strict";
global.ImageFilter = {
defaultOptions: {
imageCallback: function(){}
}
};
/* polyfills */
var extend = jQuery.extend;
function $(selector, el) {
if (typeof selector !== "string") {
return selector;
};
if (!el) { el = document; }
return el.querySelector(selector);
}
function selectMultiple(selector, el) {
if (!el) { el = document; }
return Array.prototype.slice.call(el.querySelectorAll(selector));
}
/* end of polyfills */
var ImageFilter = function() {
if (!global) {
throw new Error("window not found.");
}
if (!fabric) {
throw new Error("Fabric.JS not found.");
}
if (!jQuery) {
throw new Error("jQuery not found.");
}
if (!vex) {
throw new Error("Vex not found");
}
this.defaultOptions = {};
this.CANVAS_ID = "#_IFCanvas";
this.filters = {
'sepia': new fabric.Image.filters.Sepia(),
'brightness100': new fabric.Image.filters.Brightness({ brightness: 100 }),
'grayscale': new fabric.Image.filters.Grayscale()
};
selectMultiple("[data-imagefilter]", document).forEach(function($el) {
$el.addEventListener("click", function() {
global.ImageFilter.loadImage(this.dataset["imagefilterImage"]);
});
});
};
ImageFilter.version = "1.0";
ImageFilter.prototype.loadImage = function(imageSrc) {
var image = new Image;
image.src = imageSrc;
var canvas = document.createElement("canvas");
canvas.id = this.CANVAS_ID.replace("#", "");
canvas.width = image.width + 30;
canvas.style.left = "50%";
canvas.height = image.height + 20;
var container = document.createElement('div');
var ul = document.createElement('ul');
ul.classList.add('theme-style-none');
Object.keys(this.filters).forEach(function(filter){
var li = document.createElement('li');
var a = document.createElement('a');
a.href = "#";
a.text = filter;
a.setAttribute('data-imageFilter-filter', filter);
li.appendChild(a);
ul.appendChild(li);
})
container.appendChild(canvas);
container.appendChild(ul);
var CANVAS_ID = this.CANVAS_ID;
if (this.defaultOptions.imageCallback){
var callback = this.defaultOptions.imageCallback;
}
vex.dialog.open({
message: "",
buttons: [
extend({}, vex.dialog.buttons.YES, {
text: "Done"
}), extend({}, vex.dialog.buttons.NO, {
text: "Back"
})
],
contentCSS: {
"width": canvas.width + 20 + "px"
},
input: container
}).bind("vexClose", function() {
var imageData = $(CANVAS_ID).fabric.toDataURL('png');
callback(imageData, imageSrc);
});
self.canvas = new fabric.Canvas(CANVAS_ID.replace("#", ""));
$(CANVAS_ID).fabric = self.canvas;
self.incomingImage = image;
var imgInstance = new fabric.Image(image);
imgInstance.selectable = false;
self.canvas.selection = false; // disable group selection
self.canvas.add(imgInstance);
this.canvas = self.canvas;
$$("[data-imageFilter-filter]").forEach(function($el){
$el.addEventListener("click", function() {
global.ImageFilter.applyFilter(this.dataset["imagefilterFilter"]);
});
});
};
ImageFilter.prototype.applyFilter = function(filter){
console.log(arguments);
var currentObject = this.canvas.getObjects()[0];
currentObject.filters = [];
currentObject.filters.push(this.filters[filter]);
currentObject.applyFilters(
this.canvas.renderAll.bind()
);
};
window.onload = function() {
global.ImageFilter = new ImageFilter();
};
})(window);
/* DEMO.CSS */
body
{
background-color: RGBA(80, 80, 67, .7);
}
img {
border-style: solid;
border-width: 4px;
border-color: black;
width: 75px;
height: 75px;
}
a {
padding-left: 4px;
}
/* END DEMO.CSS */
/* imagefilter.css */
.vexContent {
width: auto;
height: auto;
}
#_IFCanvas {
display: inline-block;
}
.canvas-container {
margin: auto;
}
.theme-style-none {
list-style-type: none;
}
/*END IMAGEFILTER.CSS*/
<head>
<link href="http://github.hubspot.com/vex/css/vex.css" rel="stylesheet"/>
<link href="http://github.hubspot.com/vex/css/vex-theme-os.css" rel="stylesheet"/>
</head>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://github.hubspot.com/vex/js/vex.js"></script>
<script src="http://github.hubspot.com/vex/js/vex.dialog.js"></script>
<script src="http://fabricjs.com/lib/fabric.js"></script>
<script>vex.defaultOptions.className = "vex-theme-os";</script>
<script>
server = {
upload: function(){
console.log(arguments);
}
}
ImageFilter.defaultOptions.imageCallback = server.upload();
</script>
<a href="#" data-imagefilter data-imagefilter-image="http://i.imgur.com/uv6T1U2.jpg"><img width="50" height="50" class="example-image" id="imageToEdit" src="http://i.imgur.com/uv6T1U2.jpg" alt="image-1"/></a>
<a href="#" data-imagefilter data-imagefilter-image="http://i.imgur.com/uv6T1U2.jpg"><img width="50" height="50" class="example-image" id="imageToEdit" src="http://i.imgur.com/uv6T1U2.jpg" alt="image-1" /></a>
</body>