Cree una aplicación web con filtros similares a los de Instagram

En este tutorial, vamos a crear una aplicación web simple que le permite arrastrar una foto desde su computadora a la ventana del navegador y aplicarle filtros similares a los de Instagram. Para ello vamos a utilizar una serie de bibliotecas y complementos de JavaScript:

  • Caman.js:esta es una poderosa biblioteca de manipulación de lienzos que le permite aplicar varios efectos y filtros en una imagen. Viene con 18 filtros preestablecidos que usaremos en este ejemplo (puede crear más si lo desea);
  • Filereader.js:este es un contenedor ligero alrededor de los eventos de arrastrar y soltar de HTML5 que hace que sea mucho más fácil trabajar con ellos. También agrega un método a jQuery, para que pueda vincular los eventos a un elemento específico;
  • jQuery Mousewheel:estoy usando este complemento para desplazar el contenedor del filtro;
  • Además, estamos utilizando la última versión de jQuery en el momento de escribir este artículo.

También muchas gracias a Jenn y Tony Bot por su foto.

El HTML

El primer paso es escribir el HTML del ejemplo:

index.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 aloja el código que escribiremos en unos momentos. En la sección del encabezado, incluyo la fuente Yanone Kaffeesatz de Google Web Fonts.

JavaScript/jQuery

Para que la app funcione, tendremos que hacer lo siguiente:

  1. Aceptar una imagen al arrastrar y soltar;
  2. Crear un nuevo elemento de lienzo (original), con un tamaño máximo de 500x500px (personalizable) y guardarlo en la memoria;
  3. Escuchar por clics en los filtros. Cuando se selecciona uno:
    • Crear un clon del lienzo original;
    • Eliminar cualquier elemento de lienzo que se encuentre actualmente en la página;
    • Anexar el clon a #photo div;
    • Si el filtro seleccionado es diferente del "Normal", llame a la biblioteca Caman. De lo contrario, no haga nada;
    • Marque el filtro seleccionado con la clase "activa".
  4. Disparador el filtro "Normal".

Ahora que sabemos lo que hay que hacer, ¡comencemos a programar!

activos/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 admiten arrastrar/soltar archivos. Algunos de los filtros son computacionalmente intensivos, por lo que tendrá un poco de retraso antes de que los resultados se muestren en la pantalla. He limitado el ancho/alto máximo de la imagen a 500 px para acelerar un poco las cosas, pero puedes cambiar estos valores a tu gusto.

¡Listo!

Sería genial combinar este ejemplo con nuestro tutorial de Photobooth y terminar con una aplicación similar a Instagram real en su navegador. Pero dejaré esto como ejercicio para el lector :)