[Tutorial] Pizarra Compartida con SignalR y Canvas

Hola, vamos a seguir con SignalR, y digo seguir ya que este sería el segundo artículo, el primero lo puedes ver acá http://geeks.ms/blogs/gperez/archive/2013/02/15/tutorial-comenzando-con-signalr-y-razor-view.aspx

Buscando un ejemplo un poco más entretenido que enviar mensajes pensé que hacer una pizarra compartida, utilizando canvas y la posibilidades de signalR era lo adecuado.

La idea, como aparece en la siguiente figura, es crear una pizarra en donde distintos clientes puedan dibujar al mismo tiempo, además de cambiar los colores del trazo.

image

Backend

Veamos la creación del Backend, el cual está construido como una ASP.NET WEB API muy simple ya que solo es necesario que replique hacia los demás clientes conectados la data que proviene del browser.  Esta Data esta constituida por un Trazo que posee muchos puntos, este trazo corresponde a una línea desde el evento mousedown al evento mouseup cuando se está dibujando, veremos como funciona esto después, por ahora lo importante es la estructura que recibe estos datos.

public class Punto

{

  public string puntox { get; set; }

  public string puntoy { get; set; }

 

  public Punto(string puntox, string puntoy)

  {

    this.puntox = puntox;

    this.puntoy = puntoy;

  }

 

  public Punto()

  {

 

   }

}

Y la Clase Trazo:

public class Trazo

{

  public string color { get; set; }

  public List<Punto> puntos { get; set; }

 

  public Trazo(string color)

  {

    this.color = color;

   }

 

  public Trazo()

  {

   }

 }

Ahora revisemos la clase DibujoConn, que se encarga de manejar las peticiones desde el cliente, solo voy a utilizar los métodos onConnected y onReceived para simplificar al máximo el ejemplo:

public class DibujoConn : PersistentConnection

{

protected override Task OnConnected(IRequest request, string connectionId)

{

   return Connection.Broadcast(new Trazo("0"));

}

 

protected override Task OnReceived(IRequest request, string connectionId,
                                   
string
data)

{

    Trazo trazo = JsonConvert.DeserializeObject<Trazo>(data);

    return Connection.Broadcast(trazo);

           

}

}

Como se puede ver, cuando se establece la conexión se devuelve un nuevo trazo con valor 0 en el color, esto lo utilizo para indicarle al cliente que no es un trazo válido , solo una conexión del usuario.
Cuando se recibe datos desde el cliente, se deserializa, se asigna a un objeto trazo y de devuelve a los clientes.

Luego debemos agregar en el global Asax el endpoint:

RouteTable.Routes.MapConnection<DibujoSignalR.Dibujo.DibujoConn>("dibujo", "/dibujo");

Front-End

Estamos con Razor View, por lo que podemos utilizar sus bondades, voy a utilizar el archivo _Layout.cshtml para definir lo necesario.

<!DOCTYPE html>

<html>

<head>

    <meta charset="utf-8" />

    <meta name="viewport" content="width=device-width" />

    <title>@ViewBag.Title</title>

    @Styles.Render("~/Content/css")

    @Scripts.Render("~/bundles/modernizr")

</head>

<body>

    @RenderBody()

 

    @Scripts.Render("~/bundles/jquery")

    @Scripts.Render("~/bundles/jqueryui")

    @Scripts.Render("~/bundles/jquerysignalr")

 

    @RenderSection("scripts", required: false)

     

<script type="text/javascript">

    inicializar();

</script>

 

</body>

</html>

El archivo Index.cshtml que utiliza este layout es el que contiene el canvas para el dibujo:

@{

    ViewBag.Title = "Pizarra";

}

 

<h2>Pizarra – Chalalo Land</h2>

       <div id="contenedor">

            <div id="no_html5">

              <h2>Tu navegado no soporta Canvas-HTML5</h2>

            </div>

            <div id="contenedor_pizarra">

<canvas id="pizarra" width="600px" height="500px"></canvas>
<
br
/>

<a href="javascript:borrar()" id="borrar_bt">Borrar Pizarra</a>

</div>

</div>

<div id="botones">

 <div class="paleta">

    <button class="boton" id="Black">Negro</button>

    <button class="boton"id="Red">Rojo</button>

    <button class="boton"id="Green">Verde</button>

    <button class="boton" id="Blue">Azul</button>

    <button class="boton" id="White">Goma</button>

  </div>

</div>

@section Scripts { <script src="~/Scripts/pizarra.js"></script> }

Veamos el archivo pizarra.js que se lleva el peso del funcionamiento, puse la explicación en comentarios:

var pizarra_canvas;

var pizarra_context;

/*Definir el objeto trazo*/

var trazo = {

    "color": "#000",

    "puntos": []

};

 

/* Inicializa y determina si el navegador soporta html5- canvas*/

function inicializar() {

    if (!Modernizr.canvas) {

        document.getElementById("contenedor_pizarra").style.display = "none";

 

    } else {

        /* Se establece el ancho de Linea y los Listener para cuando se presiona el mouse

        */

        document.getElementById("no_html5").style.display = "none";

        pizarra_canvas = document.getElementById("pizarra");

        pizarra_context = pizarra_canvas.getContext("2d");

        pizarra_context.lineWidth = 3;

        pizarra_canvas.addEventListener("mousedown", empezarPintar, false);

        pizarra_canvas.addEventListener("mouseup", terminarPintar, false);

      

        /* Este código es para cambiar el color de la linea con respecto a

        el botón presionado, el botón tiene su color en el Id, por lo que se

        asigna a la propiedad de la linea*/

        $(".boton").click(function () {

            pizarra_canvas = document.getElementById("pizarra");

            pizarra_context.strokeStyle = this.id;

            color = this.id;

        });

        /*Esto es implemente para darle el color al botón*/

        $(".boton").each(function (index) {

            $(this).css("background-color", this.id);

        });

    }

}

 

/*En esta función se almacena el primer punto (posición)con el

 que se comenzará a pintar   */

function empezarPintar(e) {

    pizarra_context.beginPath();

    var puntox = e.clientX – pizarra_canvas.offsetLeft;

    var puntoy = e.clientY – pizarra_canvas.offsetTop;

    pizarra_context.moveTo(puntox, puntoy);

    pizarra_context.stroke();

    pizarra_canvas.addEventListener("mousemove", pintar, false);

    trazo.puntos.length = 0;

    var punto = {

        "puntox": puntox,

        "puntoy": puntoy

        };

    trazo.puntos.push(punto);

}

/* Al terminar de pintar, se envía la información (color y trazos) mediante la conexión que provee signalr a la web api,*/

function terminarPintar(e) {

   pizarra_canvas.removeEventListener("mousemove", pintar, false);

    var conn = $.connection("/dibujo");

    trazo.color = pizarra_context.strokeStyle;

    conn.start()

      .promise()

      .done(function () {

           conn.send(JSON.stringify(trazo));

    

      });

}

/* Esta función genera los puntos del trazo, cada uno se va guardando

en la conexión de puntos del trazo para luego transmitirse */

 

function pintar(e) {

    var puntox = e.clientX – pizarra_canvas.offsetLeft;

    var puntoy = e.clientY – pizarra_canvas.offsetTop;

    pizarra_context.lineTo(puntox, puntoy);

    var mipunto = {

        "puntox": puntox,

        "puntoy": puntoy

    };

    pizarra_context.stroke();

    trazo.puntos.push(mipunto);

}

 

 

function borrar() {

 

    pizarra_canvas.width = pizarra_canvas.width;

    pizarra_context.lineWidth = 3;

}

 

$(function () {

    var conn = $.connection("/dibujo");

    /*Esta es la función  que recibe el trazo de todos los clientes conectados. El objeto trazo contiene el color y los puntos, los que se recorren medianteun ciclo for para dibujarlos en pantalla*/

   

conn.received(function (data) {

if (data.color != "0") {

    pizarra_context.beginPath();

    pizarra_context.moveTo(data.puntos[0].puntox , data.puntos[0].puntoy);

    pizarra_context.strokeStyle = data.color;

    for (var i = 1; i < data.puntos.length; i++) {

     pizarra_context.lineTo(data.puntos[i].puntox , data.puntos[i].puntoy );

     pizarra_context.stroke();

    }

  }

});

 

    conn.error(function (error) {

        console.warn(error);

    });

 

    conn.start();    

});

Veamos un video de lo que se puede conseguir con este código, el nivel de abstracción que nos permite signalR es simplemente genial.

 

Ahora si quieres descargar el ejemplo:

DESCARGALO ACÁ

Espero que te sirva:
@chalalo

Un comentario en “[Tutorial] Pizarra Compartida con SignalR y Canvas”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *