WebCanvas es la clase que nos permite utilizar en nuestras aplicaciones Web el elemento Canvas de HTML5. De hecho, es un Canvas en toda regla. Ahora bien, la clase no nos proporciona un modo sencillo en el que podamos recuperar la imagen subyacente, presentada por el elemento, de modo que podamos guardarla como archivo en el disco del usuario (cliente que está usando nuestra app web a través de un navegador). Sin embargo, con algo de ingenio tenemos la solución a nuestro alcance, y es la que te presento a continuación.
Para seguir este tutorial necesitarás crear un proyecto Web desde cero. A continuación, añade un elemento de WebCanvas desde el panel Library hasta la página por omisión WebPage1
. Con el WebCanvas seleccionado, al que cambiaremos el nombre por cCanvas
en el Panel Inspector, añade el manejador de evento Paint
(Insert > Event Handler
). Aquí es donde, a efectos de este ejemplo, nos limitaremos a rellenar el canvas con el color negro, introduciendo para ello el siguiente código en el Editor de Código resultante:
g.FillRect 0,0, g.Width, g.Height
Obviamente, puedes dibujar algo mucho más creativo en tus WebCanvas o incluso pintar una imagen sobre el lienzo. El resultado será el mismo. Por ejemplo, sustituyendo la línea del evento Paint por la siguiente, dibujaremos una imagen añadida al proyecto con el nombre de archivo “landscape”.
g.DrawPicture landscape, 0, 0, g.Width, g.Height
Recuperar los datos del WebCanvas
Ahora viene el primer paso de lo realmente divertido. Con nuestra instancia WebCanvas mostrando su dibujo tenemos que hallar el modo de recuperar la información subyacente… y para eso tenemos que recurrir a un poco de código JavaScript. Este paso del proceso será el que ejecutemos desde el manejador de evento Action
correspondiente a un botón que añadiremos a la página web y que será el encargado de disparar la acción de guardar la imagen.
Una vez añadido el botón desde el panel Library a la página web WebPage1
, introduce el siguiente código en su manejador de evento Action
:
Dim js() As String js.Append "var canvas = document.getElementById('" + cCanvas.ControlID + "_canvas');" // obtenemos el elemento basado en su ID js.Append "var img = canvas.toDataURL(""image/png;base64;"");" // accedemos a la imagen del elemento, codificada como base64 js.Append "document.location.hash=img" // la asignamos a la propiedad Hash del documento, de modo que podamos recuperarla Dim execute As String = Join(js,"") ExecuteJavaScript execute // ejecutamos el código JavaScript
Como puedes ver en la primera línea, el primer paso consiste en recuperar el elemento desde el DOM de nuestra página web. Para ello utilizamos la propiedad ControlID
sobre nuestro objeto WebCanvas, añadiendo el sufijo “canvas” que es el que añade por omisión Xojo a todos los elementos de este tipo; de lo contrario obtendríamos un error si sólo nos basáramos en el ControlID
.
Una vez que tenemos la referencia a nuestro control desde código, en la siguiente línea nos encargamos de asignar a la variable img
el contenido propiamente dicho de nuestro Canvas; eso sí, en formato de URL con los datos codificados en Base64. Este es el pequeño truco que nos permitirá recuperar y tratar posteriormente la información ya desde nuestro código Xojo.
Por último, asignamos los datos obtenidos a la propiedad Hash del documento. Este es el componente que, en principio, nos permite saltar a cualquier parte etiquetada dentro de la página web en curso. Sin embargo, nosotros utilizaremos dicha propiedad como repositorio temporal para el paso de datos entre el código HTML/JavaScript y nuestro código Xojo. ¿Por qué? Pues porque en el objeto Session del proyecto podemos añadir el manejador de evento HashTagChanged que se disparará cada vez que se detecte el cambio en el valor de dicha propiedad, y de hecho podemos recuperar los datos de la propiedad Hash
.
Recuperar los datos de imagen, ya en código Xojo
Por tanto el siguiente paso consiste en añadir el manejador de evento HashTagChanged
en el objeto Session de nuestro proyecto de ejemplo; introduciendo el siguiente código en el Editor de Código resultante:
Dim datos As String = DecodeBase64(Hashtag.NthField(",",2) ) // recuperamos la información guardada en el Hash, pero sólo la parte correspondiente a los datos de imagen wp = New WebPicture(datos,"My_Picture.png") // creamos una nueva instancia de WebPicture utilizando lo datos de imagen y asignando un nombre de archivo wp.ForceDownload = True // forzamos la descarga del archivo cuando se muestre ShowURL wp.URL // y mostramos la imagen, disparando en realidad su descarga. Hashtag = "" // vaciamos el contenido de la propiedad, para asegurar que se dispare de nuevo el evento aunque no cambie la imagen.
Como puedes ver, en la primera línea nos encargamos de recuperar los datos de la imagen. Sin embargo lo hacemos utilizando NthField
porque originalmente recuperábamos la información del Canvas en formato de URL, de modo que así es como nos limitamos a recoger únicamente los datos de imagen propiamente dichos, despreciando la información correspondiente al URL. Además, coomo originalmente la información estaba codificada en Base64, aquí aplicamos la función opuesta para recoger en la variable los datos de imagen ya decodificados.
Una vez que tenemos los datos, podemos utilizar uno de los constructores disponibles en la clase WebPicture para crear una nueva imagen a partir de dichos datos, y asignando un nombre de archivo por omisión. De hecho, utilizamos esta clase porque es derivada de WebFile, lo que nos proporciona la ventaja de forzar su descarga como archivo, en vez de mostrar el contenido en la página web, de un modo realmente sencillo.
Esto es lo que hacemos precisamente, asignando el valor True
a su propiedad ForceDownload
; de modo que cuando, en la línea siguiente, empleamos ShowURL
lograremos forzar la descarga del archivo en vez de que se muestre la imagen en una nueva página web. ¡Problema resuelto!
Asegurar el ámbito durante la descarga
Sólo queda un último detalle. Probablemente te habrás fijado de que la variable wp
no está definida en el anterior fragmento de código. Esto es porque hemos de asegurarnos de que no se destruya antes de que, efectivamente, se guarde el archivo a disco. Si nos limitásemos a declarar la variable en el fragmento de código, el resultado sería un mensaje de error indicando que no se ha encontrado el archivo.
La forma de solucionar el problema es bien sencilla. Crea una nueva propiedad en el objeto Session con los siguientes valores:
- Name: wp
- Type: WebPicture
- Scope: Private
¡Y eso es todo! Ahora, cuando ejecutes la aplicación web y pulses sobre el botón, verás como el navegador descarga a disco el archivo correspondiente a la imagen mostrada por el WebCanvas (un rectángulo de color negro, en este caso).
Por tanto, en este tutorial has podido ver uno de los modos en los que podemos “enlazar” las modificaciones o valores obtenidos mediante la ejecución de código JavaScript en nuestra página web, para su posterior intercepción y tratamiento ya desde la parte de código Xojo; así como la flexibilidad que nos aporta poder ejecutar directamente código JavaScript sobre los elementos que componen nuestros diseños.