Recortes de imágenes (crop) con HTML5, Canvas y jQuery
- Andrés Cruz
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
ye.clientY
. - El método
offset()
permite obtener las coordenadas X y Y relativas a un elemento. - El método
scrollLeft()
yscrollTop()
permite obtener la posición de la barra de desplazamiento de un elemento; en este caso delbody
.
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 FuenteExiste 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
Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter