Jul 052012
 
Artículo HTML

( Read this post in english )

En este artículo vamos a ver cómo se puede definir en nuestra página web un área en la que el usuario puede dibujar formas con el ratón. Después, la imagen dibujada puede ser enviada al servidor para que la procese de la forma que se desee. Esta funcionalidad la utilizaremos en nuestro diccionario mandarín-español para permitir al usuario escribir con el ratón el símbolo hanzi que desea localizar. Para este propósito necesitaremos también utilizar un software de reconocimiento de caracteres (OCR) con soporte para chino simplificado, que comentaremos en un próximo artículo.

1. Código HTML

En primer lugar, preparamos el código HTML de la página, que debe contener un elemento <div> vacío (en el cual se cargará el canvas mediante javascript), y también debe cargar la librería jquery que vamos a utilizar para la manipulación de los elementos del DOM:

<html>
<head>
<script type="text/javascript" src="/js/jquery-1.7.2.min.js"></script>
<!--[if IE]><script type="text/javascript" src="/excanvas.js"></script><![endif]-->
<script type="text/javascript">
    // Variables para contener los sucesivos puntos (x,y) por los que va
    // pasando el ratón, y su estado (pulsado/no pulsado)
    var movimientos = new Array();
    var pulsado;
    function crearLienzo() {
        //Aquí es donde vamos a insertar el código javascript para crear el lienzo
    }
    function repinta() {
        // función para dibujar en el lienzo los movimientos del ratón que hemos
        // recogido en la variable "movimientos"
    }
</script>
</head>
<body style="background: #eee;" onload="crearLienzo();">
<div id="lienzo" style="width: 200px; height: 200px; background: #fff;"></div>
</body>
</html>

Como se puede ver, también estamos cargando la librería “excanvas” para dar soporte a Internet Explorer. En el elemento <body> hemos añadido también la llamada onload=”crearLienzo();” para inicializar el área de dibujo.

2. Código javascript de inicialización y manejo de eventos

El código para crear el lienzo (en inglés “canvas”) es el siguiente:

        var canvasDiv = document.getElementById('lienzo');
        canvas = document.createElement('canvas');
        canvas.setAttribute('width', 200);
        canvas.setAttribute('height', 200);
        canvas.setAttribute('id', 'canvas');
        canvasDiv.appendChild(canvas);
        if(typeof G_vmlCanvasManager != 'undefined') {
            canvas = G_vmlCanvasManager.initElement(canvas);
        }
        context = canvas.getContext("2d");

A continuación, también dentro de la rutina de inicialización, debemos añadir el código para procesar las acciones que realice el usuario con el ratón: pulsar el botón, mover el ratón y dejar de pulsar el botón. Esto lo hacemos asociando funciones javascript a los eventos mousedown, mousemove y mouseup respectivamente:

        $('#canvas').mousedown(function(e){
          pulsado = true;
          movimientos.push([e.pageX - this.offsetLeft,
              e.pageY - this.offsetTop,
              false]);
          repinta();
        });

        $('#canvas').mousemove(function(e){
          if(pulsado){
              movimientos.push([e.pageX - this.offsetLeft,
                  e.pageY - this.offsetTop,
                  true]);
            repinta();
          }
        });

        $('#canvas').mouseup(function(e){
          pulsado = false;
        });

        $('#canvas').mouseleave(function(e){
          pulsado = false;
        });
        repinta();
    }

Conforme el usuario mueve el ratón, se generan eventos ‘mousemove’, y en el array ‘movimientos’ vamos guardando tripletes [x, y, estado], en donde (x,y) son las coordenadas en donde se encuentra el cursor, y estado es “true” si el botón del ratón está pulsado en ese momento.

3. actualización del área de dibujo

Por último, la función ‘repinta’ es llamada cada vez que se añade una entrada al array ‘movimientos’, y se encarga de trasladar al lienzo los movimientos recogidos:

function repinta(){
  canvas.width = canvas.width; // Limpia el lienzo

  context.strokeStyle = "#0000a0";
  context.lineJoin = "round";
  context.lineWidth = 6;

  for(var i=0; i < movimientos.length; i++)
  {     
    context.beginPath();
    if(movimientos[i][2] && i){
      context.moveTo(movimientos[i-1][0], movimientos[i-1][1]);
     }else{
      context.moveTo(movimientos[i][0], movimientos[i][1]);
     }
     context.lineTo(movimientos[i][0], movimientos[i][1]);
     context.closePath();
     context.stroke();
  }
}

Y este es el resultado que obtenemos:

4. Envío de la imagen al servidor

Mediante la función ‘toDataURL’ podemos convertir el contenido del lienzo en una imagen codificada en base64 (por defecto, en formato png), y enviarlo al servidor como el valor de una variable de formulario (utilizando el método POST para no tener problemas con el tamaño de la imagen). Suponiendo que en el servidor existe un script llamado ‘upload-imagen.php’ que va a recibir la imagen y depositarla en la url ‘/uploads/imagen.png’, la función javascript que realizará el envío podría ser:

function upload() {
    $.post('/upload-imagen.php',
        {
        img : canvas.toDataURL()
        },
        function(data) { 
        // Cuando ha finalizado el envío, presenta en pantalla la imagen que ha
        // quedado almacenada en el servidor
        $('#imagen').attr('src', '/uploads/imagen.png?timestamp=' + new Date().getTime());
        });
}

Sólo nos queda añadir en nuestro código HTML un botón para llamar a la función ‘upload’, y un elemento <img> para presentar la imagen una vez que ha sido enviada al servidor:

<input name="guardar" type="button" value="guardar" onclick="upload();">
<img style="width:60px; height:60px" id="imagen" src="/uploads/imagen.png" />

Nota: La carga de la imagen recién enviada al servidor se hace añadiendo un argumento ‘timestamp’, cuyo valor cambia cada vez que se realiza la carga. Esto no es absolutamente necesario, pero evita problemas en caso de que el navegador o algún servidor proxy guarde la imagen en cache, ya que con el argumento ‘timestamp’ obligamos a que se solicite siempre la imagen al servidor. El script ‘upload-imagen.php’ que recibe la imagen y la guarda en el servidor es como sigue:

<?php

if (array_key_exists('img',$_REQUEST)) {
    echo $_REQUEST['img'];

    // convierte la imagen recibida en base64
    // Eliminamos los 22 primeros caracteres, que 
    // contienen el substring "data:image/png;base64,"
    $imgData = base64_decode(substr($_REQUEST['img'],22));

    // Path en donde se va a guardar la imagen
    $file = '/uploads/image.png';

    // borrar primero la imagen si existía previamente
    if (file_exists($file)) { unlink($file); }

        // guarda en el fichero la imagen contenida en $imgData
        $fp = fopen($file, 'w');
        fwrite($fp, $imgData);
        fclose($fp);
}
?>

5. Dibujar en un canvas HTML5 en dispositivos móviles

En dispositivos móviles, el usuario no utiliza un ratón. En su lugar, interacciona con la aplicación a través de una pantalla táctil. El procedimiento para dibujar sobre el canvas en un dispositivo móvil es muy similar al explicado en este artículo, pero en lugar de capturar y procesar los eventos de ratón generados en un ordenador de sobremesa o portátil, hay que utilizar los eventos específicos de una pantalla táctil. El lector interesado puede consultar en este mismo blog el artículo sobre cómo dibujar con el dedo en un canvas HTML5 en un dispositivo móvil.

 

—-

 Publicado por en 8:55 am

  3 Respuestas a “Cómo dibujar con el ratón en un canvas HTML5”

  1. […] un artículo anterior hemos visto cómo dibujar gráficos con el ratón sobre un “lienzo” (canvas) HTML5. También hemos presentado la manera de instalar el […]

  2. Podrías dejar un ejemplo?

    • Hola Jose,

      El ejemplo está al final del apartado 3 del artículo. En él se incluye un iframe que contiene el lienzo sobre el que puedes pintar con el ratón.

 Deja un comentario

(requerido)

(requerido)