Aerógrafo

facebook-svg gplus-svg twitter-svg

Todo el mundo sabe que es un aerógrafo: un dispositivo que pulveriza un fino rocío de pintura. Podemos simular un "aerógrafo" en el canvas de HTML5 utilizando el método getImageData(), un ejemplo práctico de como manipular una imagen.

Qué necesitamos

Primero necesitamos poder detectar la posición del ratón que es "la boquilla del aerografo" con el cual dibujamos.


function oMousePos(canvas, evt) {
  var ClientRect = canvas.getBoundingClientRect();
  return { //objeto
  x: Math.round(evt.clientX - ClientRect.left),            
  y: Math.round(evt.clientY - ClientRect.top) 
}

También tenemos que establecer algunas variables: la densidad de puntos del aerógrafo, y el radio pulverizado.

var densidad_aerografo = 50;
var radio_aerografo = 10;

Asimismo establecemos la variable

var dibujar = false

Se trata de un booleano que nos permite dibujar ( si true ), o no ( si false ).

También necesitamos utilizar algunos eventos del ratón:

  • 1. Al presionar el botón del ratón empezamos a dibujar ( dibujar = true; ). El evento del ratón involucrado en este caso es mousedown ( literalmente : ratón abajo ).
  • 2. Al mover el ratón dibujamos. El evento del ratón involucrado en este caso es mousemove ( literalmente : ratón moviéndose ).
  • 3. Al soltar el botón del ratón dejamos de dibujar ( dibujar = false; ). El evento del ratón involucrado en este caso es mouseup. ( literalmente : ratón arriba ).
  • 4. Si queremos podemos también tomar en consideración la salida del ratón del canvas: mouseout ( literalmente : ratón fuera ).

canvas.addEventListener('mousedown', function(evt) {
  dibujar = true;
}, false);

canvas.addEventListener('mouseup', function(evt) {
  dibujar = false;
}, false);

canvas.addEventListener("mouseout", function(evt) {
  dibujar = false;
}, false);

canvas.addEventListener("mousemove", function(evt) {
  if (dibujar) {
    var m = oMousePos(canvas, evt);
    // toda la funcionalidad del aerógrafo va aquí
    Aerografo(m);
  }
}, false);

Empezamos

Primero iniciamos el canvas, en este caso un canvas de 300 por 300 pixeles:


var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var cw = canvas.width = 300,
    cx = cw / 2;
var ch = canvas.height = 300,
    cy = ch / 2;
    
var dibujar = false;
var densidad_aerografo = 50;
var radio_aerografo = 10;

Al iniciar el canvas todavía no podemos dibujar.

var dibujar  = false;

A continuación dibujamos un rectángulo blanco del tamaño del canvas: la imagen a manipular.


ctx.beginPath();
ctx.fillStyle = "white";
ctx.fillRect(0, 0, cw, ch);

Los píxeles de una imagen "en crudo" tienen una "anchura" de 4 bytes, uno por cada componente R G B A. ( Red ( rojo ), Green ( verde ), Blue ( azul ) y Alpha ( transparente ) ). Accediendo uno por uno los pixeles de una imagen podemos modificar estos componentes de color, y por tanto manipular el aspecto de las imágenes en el canvas.

Lea más acerca de cómo manipular una imágen en canvas

Pero primero tenemos que recuperar los datos de los píxeles del rectángulo especificado, y lo hacemos utilizando el método getImageData():

var imgData = ctx.getImageData(0, 0, cw, ch);
var pixels = imgData.data;

La función Aerografo()

Al mover el ratón, si dibujar == true, llamamos a la función Aerografo(). Practicamente toda la funcionalidad va aquí.
El ratón representa la boquilla del aerógrafo, y la variable densidad_aerografo representa el número de finas partículas de "pintura" que salen por la boquilla en cada momento.
Para cada una de estas partículas:

for (var p = 0; p < densidad_aerografo; p++) {. . .

la función Aerografo() escoge al azar:
un ángulo y una distancia desde la boquilla:

var a = Math.random() * Math.PI * 2;// el ángulo
var r = Math.random() * radio_aerografo;// la distáncia

calcula las coordenadas de la particula como un punto en la circunferencia de un circulo de radio r, cuyo centro coincide con la posición del ratón ( m ).

var x = ~~(m.x + r * Math.cos(a));
var y = ~~(m.y + r * Math.sin(a));

y encuentra el índex del pixel cuyas coordenadas son x e y en el objeto imgData. No hay que olvidar que la "anchura"  de cada pixel es de 4 bytes, uno por cada componente R G B A.

var i = (x + y * imgData.width) * 4;

A continuación la función Aerografo() modifica los componentes R G B de este pixel; en este caso cambia el color de blanco a verde : rgb(106,177,80);

pixels[i + 0] = 106; //rojo
pixels[i + 1] = 177; //verde
pixels[i + 2] = 80; //azul

Al final pone los datos modificados de nuevo en el canvas

ctx.putImageData(imgData, 0, 0);

function Aerografo(m) {
  for (var p = 0; p < densidad_aerografo; p++) {
      
      var a = Math.random() * Math.PI * 2;//el ángulo
      var r = Math.random() * radio_aerografo;// el radio
      var x = ~~(m.x + r * Math.cos(a));
      var y = ~~(m.y + r * Math.sin(a));
      var i = (x + y * imgData.width) * 4; //el index
      pixels[i + 0] = 106; //rojo
      pixels[i + 1] = 177; //verde
      pixels[i + 2] = 80; //azul
    }
    ctx.putImageData(imgData, 0, 0);
  }

Finalmente para limpiar el canvas, una función anónima vuelve a dibujar el rectángulo blanco y recupera de nuevo los datos de imgData.


limpiar.addEventListener('click', function() {
    ctx.fillRect(0,0,cw,ch);
    imgData = ctx.getImageData(0, 0, cw, ch);
    pixels = imgData.data;
  }, false);

Lo ponemos todo junto

Es recomendable abrirlo y manosearlo en odepen.io

See the Pen Manipular imágenes #6 aerografo by Gabi (@enxaneta) on CodePen.