El evento drag
Hoy en día podemos arrastrar y soltar un archivo de nuestro ordenador directamente en el navegador, y dejar al navegador que utilice este archivo, que puede ser una imagen, o un documento de texto, u otro tipo de archivos.
A continuación quiero explicar un caso en concreto: quiero coger una imagen que tengo en el escritorio y arrastrarla dentro de un elemento <canvas>
. El <canvas>
recupera la imagen, la modifica y la vuelve a poner ( con putImageData() ) en el <canvas>
. Al final utilizando el método toDataURL()
recupera el data:uri de la nueva imagen y la vuelve a utilizar.
Para empezar en el HTML tengo un <canvas>
de 300px / 300px con un borde de líneas discontinuas y un párrafo que dice "suelte aquí una imagen", Â que está justo debajo del <canvas>
.
<div id="dropzone">
<canvas></canvas>
<p class="canvasp">Suelte una imagen aquí</p>
</div>
<p id="dataUri"></p>
El CSS es nada complicado, minimalista.
#dropzone {
display: block;
width: 300px;
height: 300px;
position: relative;
margin: 50px auto 0;
padding: 0;
}
canvas {
border: 1px dashed;
font-size: 2em;
width: 300px;
height: 300px;
}
p{word-wrap: break-word;text-align: center;}
p.canvasp {
display: block;
width: 300px;
position: absolute;
top: 134px;
z-index: -1;
}
Si arrastramos una imagen de nuestro ordenador en el navegador esta se abre en una página distinta. ¡Compruébelo!
Y no nos interesa que esto pase. Así que tenemos que impedir al navegador que haga lo que hace por defecto. Para esto utilizamos los métodos event.stopPropagation()Â y event.preventDefault().
Los eventos a utilizar en este caso son:
- dragenter
( cuando la imagen que arrastramos entra en el canvas ),
- dragover
( cuando arrastramos la imagen por encima del canvas ), y
- drop
( cuando soltamos la imagen ).
Pero hay toda una lista de eventos que pueden ser utilizados en esta situación.
canvas.addEventListener("dragenter", dragenter, false);
canvas.addEventListener("dragover", dragover, false);
canvas.addEventListener("drop", drop, false);
function dragenter(e) {
 e.stopPropagation();
 e.preventDefault();
}
function dragover(e) {
 e.stopPropagation();
 e.preventDefault();
}
function drop(e) {
 e.stopPropagation();
 e.preventDefault();
}
Véalo en codepen
See the Pen Drag your file here A by Gabi (@enxaneta) on CodePen.
Ahora si soltamos la imagen justo encima del canvas no pasa nada, y es justo lo que queremos. Sin embargo si soltamos la imagen fuera del canvas el navegador abrirá la imagen en otra página.
Para poder manejar los archivos necesitamos saber más, y para esto tenemos recuperar los datos de esta imagen, y esto es relativamente fácil. El objeto dataTransfer guarda los datos de los archivos que soltamos en el canvas, y dataTransfer.files
contiene una lista de los archivos soltados.
function drop(e) { Â e.stopPropagation(); Â e.preventDefault(); var datos = e.dataTransfer; Â var archivos = datos.files; Â // ahora podemos manejar los archivos: Â manejarArchivos(archivos); }
La función manejarArchivos()
Primero utilizamos un bucle for para iterar todos los archivos soltados en el canvas.
function manejarArchivos(archivos) { for (var i = 0; i < archivos.length; i++) { Â Â Â var archivo = archivos[i]; . . . . .
Para cada archivo necesitamos comprobar si es una imagen. El MIME Type de un archivo de imagen empieza con "image/"
asi que podemos utilizar expresiones regulares para verificar si se trata de una imagen o no:
// si es una imagen empieza con "image/" var esImagen = /^image\//; Â Â Â // si no esImagen ignoralo if (!esImagen.test(archivo.type)) { Â Â Â Â Â continue; }
Pero si es una imagen queremos crear un nuevo objeto imagen:
var img = new Image();
El atributo src
de esta imagen es:
img.src = window.URL.createObjectURL(archivo);
Lea acerca del método URL.createObjectURL()
Y cuando la imagen esté cargada ( img.onload
) podremos hacer un montón de cosas, pero por ahora solo quiero dibujarla en el canvas: ctx.drawImage(this, 0, 0);
Al final llamamos el método URL.revokeObjectURL() para comunicar al navegador que hemos acabado y ya no es necesario guardar la información acerca del archivo en memoria.
img.onload = function() {
ctx.drawImage(this, 0, 0);
window.URL.revokeObjectURL(this.src);
}
function manejarArchivos(archivos) {
 for (var i = 0; i < archivos.length; i++) {
   var archivo = archivos[i];
  Â
   var esImagen = /^image\//;
  Â
   if (!esImagen.test(archivo.type)) {
     continue;
   }
  Â
   var img = new Image();
   img.src = window.URL.createObjectURL(archivo);
   img.onload = function() {
 Â
   ctx.drawImage(this, 0, 0);
  Â
   window.URL.revokeObjectURL(this.src);   Â
    }
 }
}
Véalo en codepen
See the Pen Drag your file here B by Gabi (@enxaneta) on CodePen.
Centrar la imagen
Para centrar la imagen en el canvas necesitamos conocer las coordenadas del centro del canvas (cx
y cy
):
var cw = canvas.width = 300, Â cx = cw / 2; var ch = canvas.height= 300, Â cy = ch / 2;
y la anchura y la altura de la imagen:
var w = img.width; var h = img.height;
Ahora podemos dibujar la imagen con drawImage() y centrarla:
img.onload = function() {    var w = img.width;    var h = img.height;    // centrar la imagen y dibujarla    ctx.drawImage(this, cx - w/2, cy - h/2, w, h);    // ya no es necesario guardar la información acerca del archivo en memoria    window.URL.revokeObjectURL(this.src);    }
El negativo de una imagen
Además de centrar la imagen podemos hacer más cosas, como por ejemplo manipular la imagen. En este caso he decidido poner la imagen en negativo.
function negativo(ctx,cw,ch){
 //Devuelve un objeto imgData con los datos de todos los píxeles de la imagen
 var imgData = ctx.getImageData(0, 0, cw, ch);
 var pixels = imgData.data;
 // recorre uno a uno los pixeles de la imagen y cambia el color por el complementario
 for (var i = 0; i < pixels.length; i += 4) {
    pixels[i] = 255 - pixels[i]; // rojo
    pixels[i + 1] = 255 - pixels[i + 1]; // verde
    pixels[i + 2] = 255 - pixels[i + 2]; // azul
  }
  // pone la imagen modificada en el canvas
    ctx.putImageData(imgData, 0, 0, 0, 0, cw, ch)
}
También podemos recuperar el data:uri de esta imagen, y muchisimas más cosas
img.onload = function() {  var w = img.width;  var h = img.height;  // centrar la imagen  ctx.drawImage(this, cx - w/2, cy - h/2, w, h);  //pone la imagen en negativo  negativo(ctx,cw,ch);  // recupera el data:uri de la imágen  url = canvas.toDataURL();  outputDataUri.innerHTML = url; window.URL.revokeObjectURL(this.src);
Vea este ejemplo en codepen:
See the Pen Drag your file here C by Gabi (@enxaneta) on CodePen.
Artículos relacionados
- - El evento drag
- - Arrastrar y soltar
- - Arrastrar estrellas
- - Arrastrar polígonos
Enlaces útiles
- MDN: The DragEvent interface
- MDN: Event.stopPropagation()
- MDN: Event.preventDefault()
- MDN: The DataTransfer object
- Vea la chuleta de canvas.
- Más información acerca del soporte de canvas en los navegadores