0:00 0 1
Imágenes, Canvas, HTML5 y como guardarlo en el servidor

Imágenes, Canvas, HTML5 y como guardarlo en el servidor

  DrUalcman |  agosto 242019

Dentro del proyecto de Mi Biografía  tuve la necesidad de crear una imagen que imitara un libro con su portada, el título y el nombre del autor. Y como lo he hecho es lo que en esta ocasión quiero compartir con todos. La verdad que me resultó mucho más fácil de lo que esperaba. Primero intenté hacer la composición utilizando puto HTML y CSS, y no quedó mal, tuve que utilizar gráficos y vectorización con SVG, pero no terminada de ser exactamente lo que quería, además tenía el inconveniente de que para exportarlo como un archivo se me estaba complicando bastante la cosa.

Entonces me acordé de que hace un tiempo había utilizado CANVAS para crear imágenes con textos  o incluso una utilidad de captcha directo con JAVASCRIPT y, como no, usando mi script de imágenes con textos. Y fue entonces cuando dí con la solución que aquí os presento.

Empecemos con el problema

Lo que se necesita es crear una imagen que simule un libro, para ello necesitamos la foto de portada, el título y el nombre del autor. Para ello, además, necesitaremos otra imagen que haga la simulación del libro. En mi caso he creado una imagen básica, que simula un libro a la que le he cortado la parte interior para que sea transparente, de esta forma, este imagen se superpondrá a la imagen que queremos como portada y creará el efecto deseado, la portada será visible dentro del libro.

Una vez superpuestas las dos imágenes, deberemos escribir el texto sobre la última imagen, y en mi caso, inclinar un poco para que coja el efecto de giro que tiene mi imagen que simula el libro inclidado. Esto es básicamente lo que vamos a hacer hoy.

Trabajando con el CANVAS

En este ejemplo nuestro CANVAS siempre debe de ser un cuadrado perfecto, pero se de buena tinta que sabrás como modificar el código para que funcione con tus necesidades. Para ello incluiremos en nuestro HTML un objeto CANVAS con las dimensiones deseadas, en nuestro caso 600x600.

< canvas height="600" width="600" id="micanvas" >< /canvas >

Ahora ya vamos a manejar todo desde JAVASCRIPT. Describo los pasos y mostraré el código necesario. Primero recogemos el CANVAS que va a contener nuestra composición y almacenamos el tamaño, que se utilizará más adelante para los cálculos de escalado de la imagen.

    let canvas = document.getElementById("micanvas");    //recoger el canvas que contendrá la composicion
let width = canvas.width;
let height = canvas.height;
let ctx = canvas.getContext("2d"); //crear el objeto canvas
ctx.beginPath(); //comenzar un nuevo path donde incluir los datos

Ahora creamos la imagen de la portada, recordar que ésta debe de ser la primera en cargar, ya que la que simula el libro tiene la trasparencia que mostraré esta imagen y creará el efecto deseado. Pero debemos tener en cuenta que la imagen no se muestra en el CANVAS hasta que no se ha descargado completamente, por lo que utilizaremos el evento onload de la imagen para mostrarla en el CANVAS.

    let img = new Image();
img.src = picture;
img.onload = function () {

Como hemos dicho antes, esta imagen la vamos a escalar para poder hacer que encaje dentro de nuestra imagen, para eso vamos hacer un simple cálculo de proporciones para utilizar la el método scale(x,y) del objeto CANVAS. Este cálculo es muy simple, sencillamente dividiremos el ancho del contenedor, en nuestro caso 600, entre el ancho de la imagen. Si la imagen es más ancha que el contenedor nos va a devolver un número menos que 1, es decir 0.xxxxx. Si por el contrario el contenedor el más ancho que la imagen nos devolver un número mayor que 1, es decir x.xxxxx. Realizaremos la misma operación para el alto de la imagen.

        let imgH = img.height;
let imgW = img.width;
let ratio;
//scale image
let scaleX = width / imgW;
let scaleH = height / imgH;
ctx.scale(scaleX,scaleH);

Una vez escalado ahora podemos mostrar la imagen, que ya está descargada completamente, recordar que hemos hecho esto dentro del evento onload de la imagen. Además deberemos de restaurar el estado del CANVAS después del escalado con el método  setTransform(). Guardamos el estado del CANVAS con el método save() y comenzamos un nuevo path.

        ctx.drawImage(img, 0, 0);      //x,y,width,height
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.save();
ctx.beginPath();

Ahora, aun dentro del evento onload, crearemos otra imagen y realizaremos casi los mismos pasos. En el evento onload, cuando la imagen ya está descargada, será cuando dibujaremos la imagen y continuaremos con los siguientes pasos.

        let img2 = new Image();
img2.src = cover;
img2.onload = function () {
ctx.drawImage(img2, 0, 0, width,height);
ctx.save();

Es el turno de colocar el texto. Este proceso también lo hice fuera del evento onload de la segunda imagen, la que imita el libro y tiene la transparencia, pero no quedaba exactamente como yo quería por lo que decidí dejarlo dentro de este segundo evento. Como hemos comentado antes, en nuestro ejemplo, el libro tiene como una pequeña inclinación, por lo que vamos a tener que inclinar un poco el texto. 

            //text with the title of teh book                    
ctx.beginPath();
ctx.transform(1, -0.126, 0.29, 1, 0, 0);

Formateamos el texto del título, que es lo que estamos escribiendo en este momento.

            //format text
let fontSize = "36px";
let fontFamily = "Arial";
ctx.direction = 'ltr';
ctx.font = fontSize + " " + fontFamily;

Y algo que debemos controlar es el ancho del texto, en mi caso, calcule que más de 15 caracteres por línea se saldría el texto, además de calcular dónde quiero que empiece horizontalmente y verticalmente el texto, es decir las coordenadas X e Y. Tras una cuantas pruebas me gusto como quedaba en la posición X=50 y Y=100. En las siguiente líneas realizo el cálculo para sólo escribir un máximo de 15 caracteres por línea e ir partiendo el título en palabras completas por cada línea. Se puede hacer mejor, pero eso ya os lo dejo en custras manos.

            //text start position
let x = 50;
let y = 100;
//write a text
let textos = [];
let tam = title.length;
if (tam > 15){
let words = title.split(' ');
let linea = '';
let tamLinea = 0;
let s = 0;
for (let w = 0; w < words.length; w++) {
s++;
let tamWord = words[w].length;
let test = tamLinea + tamWord + s;
if (test < 15){
linea += words[w];
tamLinea += tamWord;
}
else {
textos.push(linea);
linea = words[w];
tamLinea = 0;
s = 0;
}
linea += ' ';
}
textos.push(linea);
for (let i = 0; i < textos.length; i++) {
//text border color
ctx.strokeStyle = "white";
ctx.strokeText(textos[i], x, y);
//text color
ctx.fillStyle = "black";
ctx.fillText(textos[i], x, y);
y+=36;
}
}
else {
//text border color
ctx.strokeStyle = "white";
ctx.strokeText(title, x, y);
//text color
ctx.fillStyle = "black";
ctx.fillText(title, x, y);
}

ctx.closePath();

Es el turno para el texto del autor. Este proceso es similar al anterior, solo que la posición del texto la he alineado al margen derecho, y para ello tuve que hacer cálculos hasta encontrar la posición exacta que quería, y quedó así.

            //text with the name of the user    
ctx.beginPath();
ctx.setTransform(1, -0.156, 0.29, 1, 0, 0);
//format text
fontSize = "14px";
fontFamily = "Arial";
ctx.textAlign = 'end';
ctx.font = fontSize + " " + fontFamily;
//text start position
x = width / 1.47;
y = height - (height / 7.5);
//text border color
ctx.strokeStyle = "black";
ctx.strokeText(writer, x, y);//, width);
//text color
ctx.fillStyle = "white";
ctx.fillText(writer, x, y);//, width);
ctx.closePath();
};

ctx.closePath();
};

Como esto ya es el final de los dos eventos onload es por eso que cierro también el path anterior.

Guardar la imagen

Una vez que estamos mostrando la imagen al usuario, lo interesante es poder guardar la imagen, ya sea con un botón de descarga o enviando directamente la imagen al servidor con alguna función y un webservice. Aqui os dejo la función para hacer llamar desde un evento onclick y que se guarde en el PC del usuario. Esta función descargará directamente la imagen en el PC del usuario de la página.

function SaveImage() {
let canvas = document.getElementById("micanvas");
let link = window.document.createElement('a');
let url = canvas.toDataURL();
let filename = 'screenshot.jpg';

link.setAttribute('href', url);
link.setAttribute('download', filename);
link.style.visibility = 'hidden';
window.document.body.appendChild(link);
link.click();
window.document.body.removeChild(link);
}

Con esta otra función, y utilizando AJAX. Podreis enviar la imagen hacia un webservice para guardar la imagen directamente el servidor.

function SaveFile() {
let canvas = document.getElementById("micanvas");
let url = canvas.toDataURL('image/png');
let b64 = url.slice(url.indexOf(',') + 1);
$.ajax({
url: "webservice/SaveFile",
cache: false,
dataType: "json",
data: '{"data":"' + b64 + '"}',
type: "POST",
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log(data);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.error(XMLHttpRequest, textStatus, errorThrown);
}
});
}

Como es normal en mí, no me gusta dejar a medias los ejemplos, por lo que aquí os dejo el código para el webservice en C#, ya como manejeis ese webservice dependerá de vuestro sistema, se me olvidaba, este webservice es bajo webforms si trabajas con mvc es un poco diferente la declaración, pero el código C# es el mismo.

    [WebMethod()]
public void SaveImage(string data)
{
string bookName = "imagen.png";

byte[] imgByteArray = Convert.FromBase64String(data);

ficheros f = new ficheros();
f.guardaDato(bookName, imgByteArray, "images", false);
f = null;
}

Para que no te quedes a medias y te preguntes de dónde sale esa clase ficheros y el método guardaDato, aquí tienes el código dentro de la clase funciones.

	    /// 
/// Save file in server. Return the name of file.
///

/// Name of file
/// File
/// Folder to save
/// Generate dinamic name
/// Indica si el archivo esta en formato Hexadecimal
///
public string guardaDato(string NombreArchivo, byte[] Archivo, string Carpeta = "", bool NombreDinamico = false, string pre = "")
{
//string strRuta;
string guardar = "";
Carpeta = HttpContext.Current.Server.MapPath("~/" + Carpeta);
archivos a = new archivos();
guardar = a.guardaDato(NombreArchivo, Archivo, Carpeta, NombreDinamico, pre);
a = null;
return guardar;
}

Código al completo

Bueno, aunque os dejo los archivos para descargar, aquí tienes el código completo del ejemplo, la parte de C# no, porque depende mucho de vuestro código, por lo que el ejemplo es sólo puto HTML y JAVASCRIPT.

index.html
< !DOCTYPE html >
< html lang="en" >
< head >
< meta charset="UTF-8" >
< meta name="viewport" content="width=device-width, initial-scale=1.0" >
< meta http-equiv="X-UA-Compatible" content="ie=edge" >
< title > Book Cover < /title >
< !-- Styles -->
< link rel="stylesheet" href="css/main.css" >
< script type="text/javascript" src="js/mycanvas.js" >< /script >
< /head >
< body >
< canvas height="600" width="600" id="micanvas" >< /canvas >
< br />
< span onclick="SaveImage()" >Download Click Here< /span >
< !-- Scripts -->
< script >
myCanvas('I get the book already... yeah!!!','Sergi Ortiz Gomez','imgs/dog.png','imgs/cover.png');
< /script >
< /body >
< /html >
mycanvas.js
function myCanvas(title, writer, picture, cover) {
let canvas = document.getElementById("micanvas"); //recoger el canvas que contendrá la composicion
let width = canvas.width;
let height = canvas.height;
let ctx = canvas.getContext("2d"); //crear el objeto canvas
ctx.beginPath(); //comenzar un nuevo path donde incluir los datos
let img = new Image();
img.src = picture;
img.onload = function () {
let imgH = img.height;
let imgW = img.width;
let ratio;
//scale image
let scaleX = width / imgW;
let scaleH = height / imgH;
ctx.scale(scaleX,scaleH);
ctx.drawImage(img, 0, 0); //x,y,width,height
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.save();
ctx.beginPath();

let img2 = new Image();
img2.src = cover;
img2.onload = function () {
ctx.drawImage(img2, 0, 0, width,height);
ctx.save();

//text with the title of teh book
ctx.beginPath();
ctx.transform(1, -0.126, 0.29, 1, 0, 0);
//format text
let fontSize = "36px";
let fontFamily = "Arial";
ctx.direction = 'ltr';
ctx.font = fontSize + " " + fontFamily;
//text start position
let x = 50;
let y = 100;
//write a text
let textos = [];
let tam = title.length;
if (tam > 15){
let words = title.split(' ');
let linea = '';
let tamLinea = 0;
let s = 0;
for (let w = 0; w < words.length; w++) {
s++;
let tamWord = words[w].length;
let test = tamLinea + tamWord + s;
if (test < 15){
linea += words[w];
tamLinea += tamWord;
}
else {
textos.push(linea);
linea = words[w];
tamLinea = 0;
s = 0;
}
linea += ' ';
}
textos.push(linea);
for (let i = 0; i < textos.length; i++) {
//text border color
ctx.strokeStyle = "white";
ctx.strokeText(textos[i], x, y);
//text color
ctx.fillStyle = "black";
ctx.fillText(textos[i], x, y);
y+=36;
}
}
else {
//text border color
ctx.strokeStyle = "white";
ctx.strokeText(title, x, y);
//text color
ctx.fillStyle = "black";
ctx.fillText(title, x, y);
}

ctx.closePath();
//text with the name of the user
ctx.beginPath();
ctx.setTransform(1, -0.156, 0.29, 1, 0, 0);
//format text
fontSize = "14px";
fontFamily = "Arial";
ctx.textAlign = 'end';
ctx.font = fontSize + " " + fontFamily;
//text start position
x = width / 1.47;
y = height - (height / 7.5);
//text border color
ctx.strokeStyle = "black";
ctx.strokeText(writer, x, y);//, width);
//text color
ctx.fillStyle = "white";
ctx.fillText(writer, x, y);//, width);
ctx.closePath();
};

ctx.closePath();
};
}

function SaveImage() {
let canvas = document.getElementById("micanvas");
let link = window.document.createElement('a');
let url = canvas.toDataURL();
let filename = 'screenshot.jpg';

link.setAttribute('href', url);
link.setAttribute('download', filename);
link.style.visibility = 'hidden';
window.document.body.appendChild(link);
link.click();
window.document.body.removeChild(link);
}

function SaveFile() {
let canvas = document.getElementById("micanvas");
let url = canvas.toDataURL('image/png');
let b64 = url.slice(url.indexOf(',') + 1);
$.ajax({
url: "webservice/SaveFile",
cache: false,
dataType: "json",
data: '{"data":"' + b64 + '"}',
type: "POST",
contentType: "application/json; charset=utf-8",
success: function (data) {
console.log(data);
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.error(XMLHttpRequest, textStatus, errorThrown);
}
});
}

Eso es todo amigos. Adjunto toda la documentación leída para lograr esto. Puedes verlo en funcionamiento en este enlace.

happy coding.

#HTML #javascript #CSHARP #web #trucos

1 Comentarios

 
 
 

Archivo