Redimensionamiento y recorte de imágenes con Canvas - Parte 1

En este tutorial vamos a aprender como redimensionar y recortar una imagen utilizando el elemento canvas de HTML5, y ya que estamos en ello, vamos a desarrollar unos controles para llevar a cabo la acción, comúnmente vistos en aplicaciones de edición de fotos.

En un ejemplo real de un sitio web o de una aplicación podrías usar esta técnica para cambiar el tamaño de una imagen de perfil antes de subirla. Hacer esto del lado del servidor, si la imagen es demasiado grande, puede dar lugar a un proceso muy, pero que muy lento. Por el contrario, tenemos que tener en cuenta de que podemos cambiar el tamaño de la imagen en el lado del cliente antes de subirla, que, por supuesto, es un proceso mucho más rápido.

Vamos a desarrollar esto mediante la creación de un elemento canvas de HTML5 y dibujando la imagen en el canvas, a continuación, vamos extraer los nuevos datos de la imagen del canvas como una data URI. La mayoría de los navegadores soportan dichos métodos, por lo que puedes utilizar esta técnica con normalidad, sin embargo, debes tener en cuenta ciertas limitaciones que no están relacionadas con el soporte del navegador, como la calidad de la imagen y el rendimiento.

Cambiar el tamaño de imágenes de gran tamaño puede hacer que el navegador vaya mucho más lento o, en algunos casos, incluso que se bloquee. Es por eso que tiene sentido establecer límites razonables en cuanto al tamaño del archivo a la hora de subirlo.

Una vez todo aclarado, vamos a empezar.

El HTML

En nuestro ejemplo, vamos a comenzar con una imagen ya existente:

<img class="resize-image" src="image.jpg" alt="Image" />

Eso es todo. Este es el HTML que necesitaremos para nuestra ejemplo.

El CSS

El CSS de nuestro ejemplo será mínimo. Primero, define los estilos para el resize-container y la imagen.

.resize-container {
    position: relative;
    display: inline-block;
    cursor: move;
    margin: 0 auto;
}

.resize-container img {
    display: block
}

.resize-container:hover img,
.resize-container:active img {
    outline: 2px dashed rgba(222,60,80,.9);
}

Después, establece la posición y el estilo para cada uno de los resize handles. Estos son los pequeños cuadrados de cada esquina de la imagen que vamos a arrastrar para redimensionar.

.resize-handle-ne,
.resize-handle-ne,
.resize-handle-se,
.resize-handle-nw,
.resize-handle-sw {
    position: absolute;
    display: block;
    width: 10px;
    height: 10px;
    background: rgba(222,60,80,.9);
    z-index: 999;
}

.resize-handle-nw {
    top: -5px;
    left: -5px;
    cursor: nw-resize;
}

.resize-handle-sw {
    bottom: -5px;
    left: -5px;
    cursor: sw-resize;
}

.resize-handle-ne {
    top: -5px;
    right: -5px;
    cursor: ne-resize;
}

.resize-handle-se {
    bottom: -5px;
    right: -5px;
    cursor: se-resize;
}

El Javascript

Mediante el Javascript empezamos a definir algunas variables e inicializamos el canvas y la imagen seleccionada.

var resizeableImage = function(image_target) {
    var $container,
    orig_src = new Image(),
    image_target = $(image_target).get(0),
    event_state = {},
    constrain = false,
    min_width = 60,
    min_height = 60,
    max_width = 800,
    max_height = 900,
    resize_canvas = document.createElement('canvas');
});

resizeableImage($('.resize-image'));

Después, creamos la función init a la cual llamaremos inmediatamente. Esta función envuelve la imagen en un contenedor, crea el controlador y hace una copia de la imagen original, la cual utilizaremos para cambiarle el tamaño. También asignamos el objeto jQuery para el elemento contenedor a una variable por lo que podemos hacerle referencia más tarde y añadir un detector de eventos mousedown para saber cuando alguien está arrastrando algo a alguno de los controladores.

var resizeableImage = function(image_target) {

// ...
    init = function(){

        // Create a new image with a copy of the original src
        // When resizing, we will always use this original copy as the base
        orig_src.src=image_target.src;

        // Add resize handles
        $(image_target).wrap('<div class="resize-container"></div>')
        .before('<span class="resize-handle resize-handle-nw"></span>')
        .before('<span class="resize-handle resize-handle-ne"></span>')
        .after('<span class="resize-handle resize-handle-se"></span>')
        .after('<span class="resize-handle resize-handle-sw"></span>');

        // Get a variable for the container
        $container =  $(image_target).parent('.resize-container');

        // Add events
        $container.on('mousedown', '.resize-handle', startResize);
    };

//...

    init();
}

Las funciones startResize y endResize solo le dicen al navegador cuando debe empezar a prestar atención al movimiento del ratón y cuando debe dejar de hacerlo.

startResize = function(e){
    e.preventDefault();
    e.stopPropagation();
    saveEventState(e);
    $(document).on('mousemove', resizing);
    $(document).on('mouseup', endResize);
};

endResize = function(e){
    e.preventDefault();
    $(document).off('mouseup touchend', endResize);
    $(document).off('mousemove touchmove', resizing);
};

Antes de iniciar el seguimiento de la posición del ratón hay que hacer una captura de las dimensiones de los contenedores y de otros datos clave. Almacenamos esto en una variable llamada event_state y la usaremos luego como punto de referencia al cambiar el tamaño de la altura y la anchura de la imagen.

saveEventState = function(e){
  // Save the initial event details and container state
  event_state.container_width = $container.width();
  event_state.container_height = $container.height();
  event_state.container_left = $container.offset().left; 
  event_state.container_top = $container.offset().top;
  event_state.mouse_x = (e.clientX || e.pageX || e.originalEvent.touches[0].clientX) + $(window).scrollLeft(); 
  event_state.mouse_y = (e.clientY || e.pageY || e.originalEvent.touches[0].clientY) + $(window).scrollTop();

  // This is a fix for mobile safari
  // For some reason it does not allow a direct copy of the touches property
  if(typeof e.originalEvent.touches !== 'undefined'){
	event_state.touches = [];
	$.each(e.originalEvent.touches, function(i, ob){
	  event_state.touches[i] = {};
	  event_state.touches[i].clientX = 0+ob.clientX;
	  event_state.touches[i].clientY = 0+ob.clientY;
	});
  }
  event_state.evnt = e;
}

En la función resizing es donde sucede casi toda la acción. Esta fucnión es invocada constantemente siempre que el usuario este arrastrando algo en uno de los contenedores de redimensión. Cada vez que se llama a esta función, se calcula el nuevo ancho y alto en base a la actual posición del ratón respecto a la posición inicial de la esquina en la que se está arrastrando.

resizing = function(e){ 
    var mouse={},width,height,left,top,offset=$container.offset();
    mouse.x = (e.clientX || e.pageX || e.originalEvent.touches[0].clientX) + $(window).scrollLeft(); 
    mouse.y = (e.clientY || e.pageY || e.originalEvent.touches[0].clientY) + $(window).scrollTop();

    width = mouse.x - event_state.container_left;
    height = mouse.y  - event_state.container_top;
    left = event_state.container_left;
    top = event_state.container_top;

    if(constrain || e.shiftKey){
        height = width / orig_src.width * orig_src.height;
    }

    if(width > min_width && height > min_height && width < max_width && height < max_height){
      resizeImage(width, height);  
      // Without this Firefox will not re-calculate the the image dimensions until drag end
      $container.offset({'left': left, 'top': top});        
    }
}

A continuación añadimos la opción de limitar las dimensiones de imagen utilizando la tecla de mayúsculas o una variable en concreto.

Por último, redimensionamos la imagen, pero sólo si la nueva anchura y altura no superan los límites de los valores mínimos y máximos que propusimos en un inicio dentro de unas variables.

Fuente: tympanus.net

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP
ARTÍCULO ANTERIOR