13.6.14

Crear un editor de imágenes sencillo con HTML5, Javascript y Canvas

Desde hace un tiempo empecé mi proceso personal de actualización a Salva 3.1, enfocado principalmente a renovar/ampliar mis conocimientos en el desarrollo web. Y cuanto más profundizo, más alucino. Hoy en día es posible hacer cosas en la web impensables hace unos pocos años (realmente pocos). Por ejemplo, ya es posible crear una página web que sea un editor de imágenes, sin usar Flash, ni Java, ni ningún plug-in o extensión al navegador (siempre que tu navegador sea moderno y soporte Canvas).

Esto es algo que funciona bastante bien en Firefox y Chrome. Aquí os dejo una pequeña aplicación web que he realizado y que hace uso de todo esto (y algunas cosas más). Simplemente buscas una imagen de tu ordenador, tu navegador la carga y desde javascript, sin subir la imagen a ningún servidor, te permite cierta capacidad de edición, para después permitir descargar la imagen, todo con un código javascript relativamente sencillo y con una complejidad técnica muy baja (en comparación con la misma aplicación para sobremesa):
Imagina que en tu web pones un Canvas (elemento ampliamente permitido en los navegadores modernos):
<canvas id="canvas_ed" width="300" height="300"></canvas>
¿Podrías cargar una imagen del sistema cliente en dicho Canvas, procesarla y volver a guardarla en del cliente directamente sin pasar por ningún tipo de aplicación en servidor? Pues la respuesta es SI.

Para empezar, la imagen se cargaría a través de un elemento de formulario tipo “file”:
<input type="file" accept="image/*" id="img_from_pc" />
 Ahora, cuando se cambia la imagen en ese input, se genera un evento “change” que se puede capturar (lo pongo suponiendo que se usa jQuery):
$(function () {
$("#img_from_pc").change(function (event)
{
var fileList=this.files;
if (fileList.length&gt;0)
{
leerImagenDeArchivoYActualizarCanvas(fileList[0]);
}
});
});
Ahí se usa el atributo “files” del elemento input[type="file"], que contendrá una lista con las imágenes seleccionadas, realmente es un objeto FileList, y cada elemento de la lista será un objeto de tipo File, que permite conocer el nombre del archivo cargado.

Si el input[type="file"] tiene algún archivo se invoca la función “ leerImagenDeArchivoYActualizarCanvas(fileList[0])”, que es una función a medida, que actuará de la siguiente manera:
  • Carga la imagen del cliente en un elemento HTML tipo <IMG> (que podremos ocultar para que no se vea).
  • Una vez que la imagen se ha cargado en el <IMG>, se vuelca la imagen del <IMG> al <CANVAS>.
¿Cómo se carga la imagen del cliente en un elemento <IMG>? Supongamos que tenemos un IMG oculto tal que así:
<img id="image_output">
Pues la función anterior debería leer la imagen y ponerla como “src” de la imagen, así de simple:
function leerImagenDeArchivoYActualizarCanvas(file)
{
var img=$("image_output")[0];
var fr = new FileReader();
fr.onload = function(e) {
img.src = e.target.result;
};
fr.readAsDataURL(file);
}
Esto utilizaría FileReader, que es un prototipo de objeto del api de HTML que permite leer archivos. Invocamos el método “readAsDataURL” con un objeto tipo File para comenzar la descarga, y como dicha descarga es asíncrona, hay que indicar en el atributo “onload” de dicho objeto que función se ejecutará (closure en este caso) cuando se termine la carga. Lo que se hace en este caso en la función asignada a “onload” es simplemente poner el resultado de la descarga (que será una URL de tipo data con la imagen codificada en base64) como src de la imagen.

Bien, ya tenemos la imagen, pero como pasamos la imagen al canvas, pues así. Añadimos lo siguiente a la función anterior:
function leerImagenDeArchivoYActualizarCanvas(file)
{
var img=$("image_output")[0];
img.onload=function () {

var canvas=$("#canvas_ed")[0];
var canvas_ctx=canvas.getContext("2d");
canvas_ctx.drawImage(img,0,0,300,300);
};

var fr = new FileReader();
fr.onload = function(e) {

img.src = e.target.result;
};
fr.readAsDataURL(file);
}
De esta manera, cuando la imagen se haya cargado se invocará al handler “onload”, que será una función (closure en este caso) que pintará el canvas de la imagen. Dentro de dicha función se usan obviamente objetos del api de Canvas.

Una vez que ya hemos modificado la imagen a nuestro antojo, podemos poner por ejemplo un enlace para descargarla directamente al cliente (PC, móvil, etc.). Eso sí, hay que tener en cuenta que no todos los navegadores soportan esta opción (se puede detectar con Modernizr). Esto funciona mejor en Firefox que en Chrome, aunque funciona en los dos.

Supongamos que tenemos dos enlaces:
<a id=”donwlink” href="" onclick="descargar();return false;">Haz click aquí para descargar imagen</a>
<a id="downlink_hidden" href="" download="imagen.png"></a>
El segundo enlace se supone que está oculto y utiliza el atributo “download” para decir que es un archivo para descargar y no una imagen. Nuevamente, esa opción no funciona del todo en todos los navegadores, y habría que comprobarla con Modernizr por ejemplo. Al hacer clic en el primero se invoca la siguiente función:
function descargar() {
var canvas=$("canvas_ed")[0];
$("#downlink_hidden").attr("href",canvas.toDataURL("image/png"))[0].click();
}
Al ejecutar esta función lo que hacemos es poner como destino del segundo enlace, es decir como “href”, la imagen en base 64 como URL de tipo data. Hay que verificar, con Modernizr por ejemplo, que existe la función “toDataURL” en el canvas y a que formatos de imagen es posible exportar (“image/png”, “image/jpeg” o “image/webp”), porque no todos los navegadores tienen dicha función (por ejemplo, ni Safari ni Internet Explorer la tienen) o no soportan exportar a todos los formatos.

Y esto es todo.