Recortes de imágenes (crop) con HTML5, Canvas y jQuery

- Andrés Cruz

Recortes de imágenes (crop) con HTML5, Canvas y jQuery
Ver demo Descargar Fuente

Poder cortar imágenes cuya operación también es conocida como crop puede ser muy útil en determinados casos, poder subdividir imágenes en trozos es una operación muy común hoy en día en cualquier ambiente y puede haber muchos casos en la que quieras que una aplicación web deba aplicar el recorte sobre una imagen y luego poder descargarla.

En este artículo veremos cómo recortar imágenes y guardarlas en nuestra computadora o dispositivo móvil con HTML5 y jQuery; en específico usaremos las siguientes tecnologías:

  • Canvas y su API en JavaScript para cortar, cargar y guardar la imagen.
  • JQuery: para el manejo de los eventos y llamadas a algunas funciones para darnos algo de soporte.

Funcionamiento del experimento de recorte de imágenes

El funcionamiento es sencillo y la podemos explicar de la siguiente manera:

  • El usuario realiza un primer clic sobre el canvas; capturamos las coordenadas del clic.
  • El usuario arrastra el ratón (sin tener el clic presionado) hasta localizarse en el punto de interés; a medida que se desplaza sobre el Canvas se mostrará un hover que representa el área de interés del corte ubicado desde el primer clic.
  • El usuario realiza un segundo clic sobre el canvas; capturamos las coordenadas del clic.
  • Es generada la imagen a partir del canvas.

Sigue el mismo procedimiento que los procesadores de imágenes como GIMP o Photoshop.

El método drawImage()

El método drawImage() permite dibujar una imagen, Canvas o video dentro de un Canvas (más información clic aquí); recibe como parámetros obligatorios:

  • Elemento: Imagen, video o canvas a usar.
  • X: Coordenada X que indica a partir de qué punto de la imagen se dibujó en el canvas.
  • Y: Coordenada Y que indica a partir de qué punto de la imagen se dibujó en el canvas.

Como vimos el el tutorial anterior: Escalado y recortando imágenes con Canvas el modo de la función varía según la cantidad de parámetros que le pasemos:

  • drawImage(img,x,y): Se pinta la imagen en el Canvas a partir de las coordenadas X y Y.
  • drawImage(img,x,y,width,height): Es copiada la imagen a partir de los puntos definidos por "x" y "y" con la anchura y altura definida por width y height.
  • drawImage(img,sx,sy,swidth,sheight,x,y,width,height): Es cortada la imagen a partir de los puntos sx y sy con la anchura y altura definida por swidth y sheight, luego es copiada la imagen al Canvas a partir de los puntos definidos por "x" y "y" con la anchura y altura definida por width y height.

1. Variables globales e inicialización

En esta parte definiremos las variables utilizadas para la elaboración de este ejemplo; pasaré por alto su explicación ya que la funciones de las mismas son explicadas en los comentarios del código mostrado a continuación:

//*** variables globales

// canvas y su contexto
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

// imagen en donde se colocará la imagen cortada
var canvasImg = document.getElementById('canvasImg');

// ruta a una imagen fuente
var imgSource = "image.jpg";

// punto X y Y del primer clic sobre el canvas
var iniX;
var iniY;

// punto X y Y del segundo clic sobre el canvas
var endX;
var endY;

// anchura y altura calculada a partir del primer y segundo clic
var imgW;
var imgH;

// hover que se surperpondran sobre el canvas segun la seleccion del usuario
var hover = document.getElementById('hoverCut');

// inicializo los componentes para el recorte de la imagen
function init() {
	// primer clic del usuario que indica la zona que voy a recortar
	iniX = -1;
	iniY = -1;

	// primer clic del usuario que indica la zona que voy a recortar
	endX = -1;
	endY = -1;

	// anchura de la imagen recortada
	imgW = -1;
	imgH = -1;

	// indica si coloco el hover
	hoverBol = false;

	// div hover
	$(hover).css("width", 0);
	$(hover).css("height", 0);

}

El método init() permite inicializar las variables y deshabilitar el hover (estableciendo su anchura y altura en cero); se realizará una llamada a este método por cada recorte que realice el usuario sobre el Canvas.

2. Dibujando una imagen en el Canvas

Ahora es definido un método que permita cargar una imagen fuente a un Canvas para poder manipularlo:

// cargo el contenido del canvas hacia un tag img
function loadImageToCanvas() {
    // creo un objeto de tipo Image y se le asigna una imagen fuente
    newImg = new Image();
    newImg.src = imgSource;

    // pinto la imagen en el canvas solo si se ha terminado de cargar 
    newImg.onload = function() {
        //reescalo el canvas al tamano de la imagen
        canvas.width = newImg.width;
        canvas.height = newImg.height;
        ctx.drawImage(newImg, 0, 0);
    };
}
  • En las líneas 4 y 5 creamos un objeto de tipo Image y le asignamos una imagen fuente.
  • En las líneas 10, 11, 12; luego de que este cargada la imagen es escalado el Canvas a las dimensiones de la imagen y se pinta la imagen en el Canvas.

3. Retornando el Contenido del Canvas hacia una Tag img (imagen)

Antes pasamos el contenido de una imagen hacia el Canvas; ahora haremos lo contrario; el método permitirá cargar el contenido del Canvas hacia una imagen; también se copiará el contenido de una imagen en un Tag a para poder descargar la imagen cortada (la final o generada por la operación de corte del usuario).

    // cargo la imagen hacia el canvas
    // guardo el canvas en una imagen 
    function saveCanvasToImage() { 
        var dataURL = canvas.toDataURL(); 
        canvasImg.src = dataURL; 
        document.getElementById("downloadImage").href = dataURL;
    }

4. Evento clic en el Canvas

El siguiente paso consiste en capturar dos clic del usuario sobre el Canvas; los mismo permitirán calcular el área rectangular para recortar la imagen:

  // clic sobre el canvas
    $(canvas).click(function(e) {
        var offset = $(this).offset();

        auxX = e.clientX - offset.left + $("body").scrollLeft();
        auxY = e.clientY - offset.top + $("body").scrollTop();

        // primer clic
        if (iniX < 0) {
            // primer punto
            iniX = auxX;
            iniY = auxY;

            $(hover).css("left", e.clientX + $("body").scrollLeft());
            $(hover).css("top", e.clientY + $("body").scrollTop());

            // segundo clic
        } else {
            // segundo punto
            endX = auxX;
            endY = auxY;

            // dimenciones de la nueva imagen
            imgW = endX - iniX;
            imgH = endY - iniY;

            // guardo la imagen en un tag img
            newImg = new Image();
            newImg.src = imgSource;

	    // cuando terminde de cargar la imagen
            newImg.onload = function() {
                // limpio el canvas (para que el corte sea limpio)
                ctx.clearRect(0, 0, newImg.width, newImg.height);

                // reescalo el canvas para que quede solo la img y ningun espacio en blanco
                canvas.width = imgW;
                canvas.height = imgH;

                // corto la imagen
                ctx.drawImage(this, iniX, iniY, imgW, imgH, 0, 0, imgW, imgH);

                // guardo el corte en una imagen
                saveCanvasToImage();
                // cargo de nuevo la imagen en el canvas
                loadImageToCanvas();

		// limpio la seleccion y los clic
                init();
            };
        }
    });

Analizando la función anterior...

Si nos fijamos, el cuerpo de la función manejada por el evento clic sobre el Canvas esta dividida en dos bloques; usamos la variable iniX como bandera para saber si se trata del primer clic (primer bloque encerrado por el if()) o el segundo (segundo bloque encerrado por el if()):

  • Si la misma es menor a cero significa que es el primer clic del usuario sobre el Canvas.
  • Si la misma es distinta de -1 significa que es el segundo clic del usuario sobre el Canvas.

El punto primordial de esta función es la de capturar la posición de los clic del usuario; de eso se encargan este par de líneas para el eje X y Y:

auxX = e.clientX - offset.left + $("body").scrollLeft();
auxY = e.clientY - offset.top + $("body").scrollTop();
  • Capturamos las coordenadas del clic del usuario sobre la ventana del navegador con e.clientX y e.clientY.
  • El método offset() permite obtener las coordenadas X y Y relativas a un elemento.
  • El método scrollLeft() y scrollTop() permite obtener la posición de la barra de desplazamiento de un elemento; en este caso del body.

También ubicamos al hover sobre el primer clic del usuario; al tener un atributo position:absolute no es necesario emplear el método offset() ya que no posee una posición relativa, sino absoluta.

5. El hover sobre el Canvas

Ahora colocaremos un hover semi-transparente desde el primer clic del usuario hasta donde se mueva el mismo en el Canvas; para eso cambiamos repetidamente la altura y anchura del hover según el usuario se mueva por el canvas; la explicación es similar al punto 4:

    // pinto un div sobre el canvas desde el primer clic del usuario 
    // hasta donde el usuario mueva el raton
    $(canvas).mousemove(function(e) {
        // si hubo un primer clic por el usuario
        if (iniX >= 0) {
            var offset = $(this).offset();

            endX = e.clientX - offset.left + $("body").scrollLeft();
            endY = e.clientY - offset.top + $("body").scrollTop();

            imgW = endX - iniX;
            imgH = endY - iniY;

           $(hover).css("width", imgW - 3);
            $(hover).css("height", imgH - 3);
        }
    });

Resultado Final

Ver demo Descargar Fuente

Existe otra forma en la que podemos aplicar recortes sobre las imágenes, aunque por supuesto no puedes generar una imagen a partir de la misma, es mediante los márgenes negativos, que simplemente es especificar márgenes negativos aunque solo puedes recortar desde los bordes y no en el medio de la imagen; la otra forma que tenemos es empleando la propiedad clip que vimos en esta entrada:

La propiedad clip-path en CSS para seleccionar regiones a mostrar en elementos

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz en Udemy

Acepto recibir anuncios de interes sobre este Blog.