En este tutorial, vamos a crear una simple aplicación web que te permita arrastrar una foto de tu ordenador a la ventana del navegador, y aplicarle filtros parecidos a los de la aplicación Instagram. Para ello vamos a utilizar una serie de librerías y plugins de JavaScript:
- Caman.js - Es una potente librería para la manioulación de canvas que te permite aplicar diferentes efectos y filtros en una imagen. Viene con 18 filtros predefinidos que vamos a utilizar en este ejemplo (puedes crear más si lo deseas)
- Filereader.js - Es un wrapper ligero que hace que sea mucho más sencillo trabajar con eventos de arrastrar y soltar de HTML5.
- jQuery Mousewheel - Utilizo este plugin para desplazar el contenedor del filtro;
- Además, para este tutorial hemos utilizado la última versión de jQuery a la hora de escribir.
El HTML
El primer paso es escribir el código HTML:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Instagram-like Filters with jQuery | Tutorialzine Demo</title>
<link href="assets/css/style.css" rel="stylesheet" />
<!-- Include the Yanone Kaffeesatz font -->
<link href="http://fonts.googleapis.com/css?family=Yanone+Kaffeesatz:400,200" rel="stylesheet" />
</head>
<body>
<h1>Instagram <b>Filters</b></h1>
<div id="photo"></div>
<div id="filterContainer">
<ul id="filters">
<li> <a href="#" id="normal">Normal</a> </li>
<li> <a href="#" id="vintage">Vintage</a> </li>
<li> <a href="#" id="lomo">Lomo</a> </li>
<li> <a href="#" id="clarity">Clarity</a> </li>
<li> <a href="#" id="sinCity">Sin City</a> </li>
<!-- 14 More filters go here -->
</ul>
</div>
<!-- Libraries -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="assets/js/filereader.min.js"></script>
<script src="assets/js/caman.full.js"></script>
<script src="assets/js/jquery.mousewheel.min.js"></script>
<script src="assets/js/script.js"></script>
</body>
</html>
Además de las bibliotecas mencionadas en la introducción, también incluyo el archivo script.js que alberga el código el cual te mostraremos a continuación. En el head, incluyo la fuente Yanone Kaffeesatz desde Google Web Fonts.
El JavaScript / jQuery
Para llevar a cabo la aplicación, vamos a hacer lo siguiente:
- Aceptar la imagen del drag & drop
- Crear un nuevo elemento canvas (original), con un máximo de 500x500px de tamaño (personalizable) y mantenerlo en la memoria
- Escuchar los clics de los filtros. Cuando se selecciona uno: Crear un clon del canvas original. Eliminar cualquier elemento de canvas que haya en la página. Anexar el clon al div #photo. Si el filtro seleccionado es diferente de "Normal", llama a la librería Caman. De lo contrario no hacer nada. Marca el filtro seleccionado con la clase "activo".
- Ejecutar el filtro "Normal".
Ahora que sabemos lo que hay que hacer, vamos a empezar a programar
assets/js/script.js
$(function() {
var maxWidth = 500,
maxHeight = 500,
photo = $('#photo'),
originalCanvas = null,
filters = $('#filters li a'),
filterContainer = $('#filterContainer');
// Use the fileReader plugin to listen for
// file drag and drop on the photo div:
photo.fileReaderJS({
on:{
load: function(e, file){
// An image has been dropped.
var img = $('<img>').appendTo(photo),
imgWidth, newWidth,
imgHeight, newHeight,
ratio;
// Remove canvas elements left on the page
// from previous image drag/drops.
photo.find('canvas').remove();
filters.removeClass('active');
// When the image is loaded successfully,
// we can find out its width/height:
img.load(function() {
imgWidth = this.width;
imgHeight = this.height;
// Calculate the new image dimensions, so they fit
// inside the maxWidth x maxHeight bounding box
if (imgWidth >= maxWidth || imgHeight >= maxHeight) {
// The image is too large,
// resize it to fit a 500x500 square!
if (imgWidth > imgHeight) {
// Wide
ratio = imgWidth / maxWidth;
newWidth = maxWidth;
newHeight = imgHeight / ratio;
} else {
// Tall or square
ratio = imgHeight / maxHeight;
newHeight = maxHeight;
newWidth = imgWidth / ratio;
}
} else {
newHeight = imgHeight;
newWidth = imgWidth;
}
// Create the original canvas.
originalCanvas = $('<canvas>');
var originalContext = originalCanvas[0].getContext('2d');
// Set the attributes for centering the canvas
originalCanvas.attr({
width: newWidth,
height: newHeight
}).css({
marginTop: -newHeight/2,
marginLeft: -newWidth/2
});
// Draw the dropped image to the canvas
// with the new dimensions
originalContext.drawImage(this, 0, 0, newWidth, newHeight);
// We don't need this any more
img.remove();
filterContainer.fadeIn();
// Trigger the default "normal" filter
filters.first().click();
});
// Set the src of the img, which will
// trigger the load event when done:
img.attr('src', e.target.result);
},
beforestart: function(file){
// Accept only images.
// Returning false will reject the file.
return /^image/.test(file.type);
}
}
});
// Listen for clicks on the filters
filters.click(function(e){
e.preventDefault();
var f = $(this);
if(f.is('.active')){
// Apply filters only once
return false;
}
filters.removeClass('active');
f.addClass('active');
// Clone the canvas
var clone = originalCanvas.clone();
// Clone the image stored in the canvas as well
clone[0].getContext('2d').drawImage(originalCanvas[0],0,0);
// Add the clone to the page and trigger
// the Caman library on it
photo.html(clone);
var effect = $.trim(f[0].id);
Caman(clone[0], function () {
// If such an effect exists, use it:
if( effect in this){
this[effect]();
this.render();
}
});
});
// Use the mousewheel plugin to scroll
// scroll the div more intuitively
filterContainer.find('ul').on('mousewheel',function(e, delta){
this.scrollLeft -= (delta * 50);
e.preventDefault();
});
});
Este ejemplo funciona en todos los navegadores que soportan drag&drop. Algunos de los filtros son intensivos computacionalmente, por lo que tendrás un poco de retraso antes de que los resultados se muestren por pantalla. He limitado el ancho y la altura máxima de la imagen a 500px con el fin de acelerar un poco las cosas, pero puedes cambiar estos valores a tu gusto.