En este post voy a explicar y a dar algunos ejemplos para convertir el contenido de una página web a una imagen, es decir, tomarle una “captura de pantalla” a la página web usando únicamente JavaScript del lado del cliente y la maravillosa librería de html2canvas.
Voy a mostrar cómo adjuntar el elemento canvas al documento, descargar la imagen, ignorar algunos elementos al tomar la captura de pantalla, respetar los estilos CSS y poner el resultado sobre un canvas existente.
La imagen anterior fue generada por esta librería, respetando estilos de la tabla, imágenes, colores, fuentes y más.
El funcionamiento de esta librería es sencillo: lee todo el DOM, es decir, todo el documento HTML y construye una representación del mismo en un elemento canvas, leyendo los estilos y formas que tiene. Los resultados, si bien no son siempre perfectos, son bastante aceptables.
Citando al autor:
En otras palabras, no toma realmente una captura de pantalla de la página, sino que construye una representación de la misma en función de las propiedades que lee del DOM.
Después de tener el elemento canvas podemos adjuntarlo a la página web o convertirlo a imagen.
Nota: todos los ejemplos que expondré puedes probarlos aquí, y el código completo de todos y cada uno están en mi GitHub. Tal vez en ocasiones me salte algún fragmento de código, pero puedes revisar el repositorio y ahí estará todo lo necesario.
Incluye la librería de html2canvas como un script normal. Puedes descargarlo por tu cuenta, en la página oficial hay dos enlaces, uno a la versión normal y otro a la minificada, la normal es para que analices el código y la minificada lista para ser usada en producción.
También puedes incluirla desde jsdelivr como lo hago en los ejemplos:
<!--
Cargar el script de html2canvas, podría ser desde un servidor
propio o como yo lo hago: desde jsdelivr
-->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-rc.1/dist/html2canvas.min.js"></script>
Al final todo queda en el tipo de aplicación que hagas.
Veamos un primer ejemplo. Para capturar la página web llamamos a la función html2canvas
pasándole un elemento HTML.
Este elemento puede ser document.body
o cualquier otro obtenido, por ejemplo, con querySelector
.
/**
* Ejemplo 1 de html2canvas para convertir el HTML de una web
* a un elemento canvas y adjuntarlo al contenido actual
*
* @author parzibyte
*///Definimos el botón para escuchar su click, y también el contenedor del canvas
const $boton = document.querySelector("#btnCapturar"), // El botón que desencadena
$objetivo = document.querySelector("#contenedor"), // A qué le tomamos la foto
$contenedorCanvas = document.querySelector("#contenedorCanvas"); // En dónde ponemos el elemento canvas
// Agregar el listener al botón
$boton.addEventListener("click", () => {
html2canvas($objetivo) // Llamar a html2canvas y pasarle el elemento
.then(canvas => {
// Cuando se resuelva la promesa traerá el canvas
$contenedorCanvas.appendChild(canvas); // Lo agregamos como hijo del div
});
});
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Tomar captura de página web</title>
</head>
<body>
<!--
En este caso le "tomamos" la foto al div. Podría ser a un div o
a cualquier elemento HTML
-->
<div id="contenedor">
<h1>Tomar captura de pantalla con html2canvas</h1>
<a href="//parzibyte.me/blog" target="_blank">By Parzibyte</a>
<p>Estamos probando la conversión de HTML a una imagen con html2canvas</p>
<img style="max-width: 100%;" src="cosmos-4112660_1280.jpg">
</div>
<!--
El botón no aparece porque está fuera del div
-->
<button id="btnCapturar">Tomar captura</button>
<!--
En este elemento vamos a poner al canvas que será generado.
-->
<div id="contenedorCanvas" style="border: 1px solid red;">
</div>
<!--
Cargar el script de html2canvas, podría ser desde un servidor
propio o como yo lo hago: desde jsdelivr
-->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-rc.1/dist/html2canvas.min.js"></script>
<!--
Después de eso, cargar el script que contiene nuestra lógica
-->
<script src="script.js"></script>
</body>
</html>
html2canvas devolverá una promesa que, al resolverse, traerá un elemento canvas
. Ese elemento podemos montarlo en el documento o convertirlo a imagen.
Puedes probar el ejemplo aquí.
La librería soporta muy bien los estilos del documento. En el siguiente ejemplo uso los estilos de Bulma CSS y agrego una notificación y una tabla.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Tomar captura de página web</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css" />
</head>
<body>
<!--
En este caso le "tomamos" la foto al div. Podría ser a un div o
a cualquier elemento HTML
-->
<div id="contenedor">
<h1 class="is-size-1">Tomar captura de pantalla con html2canvas</h1>
<a href="//parzibyte.me/blog" target="_blank">By Parzibyte</a>
<div class="notification is-primary">
<p>Estamos probando la conversión de HTML a una imagen con html2canvas</p>
</div>
<table class="table is-bordered">
<thead>
<tr>
<th>Nombre</th>
<th>Versión</th>
</tr>
</thead>
<tbody>
<tr>
<td>KitKat</td>
<td>4.4</td>
</tr>
<tr>
<td>Lollipop</td>
<td>5</td>
</tr>
<tr>
<td>Marshmallow</td>
<td>6</td>
</tr>
<tr>
<td>Nougat</td>
<td>7</td>
</tr>
<tr>
<td>Oreo</td>
<td>8</td>
</tr>
<tr>
<td>Pie</td>
<td>9</td>
</tr>
</tbody>
</table>
<img style="max-width: 100%;" src="cosmos-4112660_1280.jpg">
</div>
<!--
El botón no aparece porque está fuera del div
-->
<button id="btnCapturar">Tomar captura</button>
<!--
En este elemento vamos a poner al canvas que será generado.
-->
<div id="contenedorCanvas" style="border: 1px solid red;">
</div>
<!--
Cargar el script de html2canvas, podría ser desde un servidor
propio o como yo lo hago: desde jsdelivr
-->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/html2canvas@1.0.0-rc.1/dist/html2canvas.min.js"></script>
<!--
Después de eso, cargar el script que contiene nuestra lógica
-->
<script src="script.js"></script>
</body>
</html>
Puedes probarlo aquí.
En los ejemplos anteriores capturamos un div, no todo el cuerpo. Si queremos capturar todo el cuerpo simplemente pasamos document.body
a html2canvas
.
/**
* Ejemplo 3 de html2canvas para convertir el HTML de una web
* a un elemento canvas - Capturar todo el cuerpo del HTML, no solo un div
*
* @author parzibyte
*///Definimos el botón para escuchar su click, y también el contenedor del canvas
const $boton = document.querySelector("#btnCapturar"), // El botón que desencadena
$objetivo = document.body, // A qué le tomamos la foto
$contenedorCanvas = document.querySelector("#contenedorCanvas"); // En dónde ponemos el elemento canvas
// Agregar el listener al botón
$boton.addEventListener("click", () => {
html2canvas($objetivo) // Llamar a html2canvas y pasarle el elemento
.then(canvas => {
// Cuando se resuelva la promesa traerá el canvas
$contenedorCanvas.appendChild(canvas); // Lo agregamos como hijo del div
});
});
Pruébalo aquí.
Si quieres ignorar algunos elementos más tarde veremos cómo hcaerlo.
Si no te interesa el elemento canvas y quieres una imagen PNG entonces hay que aplicar algunos trucos para convertir el canvas a imagen, lo cual ya expliqué anteriormente.
El código es el siguiente:
/**
* Ejemplo 4 de html2canvas para convertir el HTML de una web
* a un elemento canvas - Descargar la captura como imagen PNG
*
* @author parzibyte
*///Definimos el botón para escuchar su click
const $boton = document.querySelector("#btnCapturar"), // El botón que desencadena
$objetivo = document.body; // A qué le tomamos la fotocanvas
// Nota: no necesitamos contenedor, pues vamos a descargarla
// Agregar el listener al botón
$boton.addEventListener("click", () => {
html2canvas($objetivo) // Llamar a html2canvas y pasarle el elemento
.then(canvas => {
// Cuando se resuelva la promesa traerá el canvas
// Crear un elemento <a>
let enlace = document.createElement('a');
enlace.download = "Captura de página web - Parzibyte.me.png";
// Convertir la imagen a Base64
enlace.href = canvas.toDataURL();
// Hacer click en él
enlace.click();
});
});
De esta manera no podemos el canvas en el cuerpo ni en un contenedor, sino que simplemente descargamos la imagen y la guardamos como PNG.
Prueba el ejemplo de descarga de imagen aquí.
La librería html2canvas permite ignorar algunos elementos a través de una función que se especifica en las opciones.
Esta función recibe el argumento del elemento HTML que está a punto de renderizarse. Si regresamos true
, el elemento se va a ignorar. Si no, el elemento se renderiza.
En el ejemplo se ignoran los elementos de imagen y encabezados, pero podríamos hacer una selección más precisa por clases, ids, etcétera; ya que tenemos al elemento completo.
/**
* Ejemplo 5 de html2canvas para convertir el HTML de una web
* a un elemento canvas - Ignorar algunos elementos al tomar la captura
*
* Nota: también puedes agregar el atributo data-html2canvas-ignore al elemento en cuestión
*
* @author parzibyte
*///Definimos el botón para escuchar su click, y también el contenedor del canvas
const $boton = document.querySelector("#btnCapturar"), // El botón que desencadena
$objetivo = document.querySelector("#contenedor"), // A qué le tomamos la foto
$contenedorCanvas = document.querySelector("#contenedorCanvas"); // En dónde ponemos el elemento canvas
// Agregar el listener al botón
$boton.addEventListener("click", () => {
const opciones = {
ignoreElements: elemento => {
// Una función que ignora elementos. Regresa true si quieres que
// el elemento se ignore, y false en caso contrario
const tipo = elemento.nodeName.toLowerCase();
// Si es imagen o encabezado h1, ignorar
if (tipo === "img" || tipo === "h1") {
return true;
}
// Para todo lo demás, no ignorar
return false
}
};
html2canvas($objetivo, opciones) // Llamar a html2canvas y pasarle el elemento
.then(canvas => {
// Cuando se resuelva la promesa traerá el canvas
$contenedorCanvas.appendChild(canvas); // Lo agregamos como hijo del div
});
});
Por cierto, la documentación dice que si ponemos el atributo data-html2canvas-ignore
a un elemento, el mismo será ignorado.
Prueba el código aquí.
Si ya tenemos un canvas definido, podemos poner la captura de pantalla tomada con html2canvas sobre el mismo, usando las opciones.
Específicamente indicamos la clave “canvas” en las opciones, el valor debe ser un elemento HTML de canvas.
/**
* Ejemplo 6 de html2canvas para convertir el HTML de una web
* a un elemento canvas - Poner resultado sobre canvas existente
*
* @author parzibyte
*///Definimos el botón para escuchar su click, y también el contenedor del canvas
const $boton = document.querySelector("#btnCapturar"), // El botón que desencadena
$objetivo = document.querySelector("#contenedor"), // A qué le tomamos la foto
$canvas = document.querySelector("#canvas"); // En dónde ponemos el resultado (debe ser un canvas)
// Agregar el listener al botón
$boton.addEventListener("click", () => {
const opciones = {
canvas: $canvas, // Indicar el elemento HTML
};
html2canvas($objetivo, opciones) // Llamar a html2canvas y pasarle el elemento con las opciones
.then(canvas => {
// Aquí ha terminado y ha puesto resultado en el canvas que le indicamos
});
});
Pruébalo aquí.
Solo resta decir que esta librería es muy poderosa y útil en varios aspectos. Te dejo un enlace a la documentación oficial para las opciones, en donde puedes ver cómo cambiar el color de fondo, cortar el canvas, etcétera.
Muy pronto traeré algunos otros ejemplos de esta librería, te invito a suscribirte al blog y seguirme en todas las redes.
Hoy te voy a presentar un creador de credenciales que acabo de programar y que…
Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…
En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…
En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…
Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…
En este artículo te voy a enseñar cómo usar un "top level await" esperando a…
Esta web usa cookies.
Ver comentarios
Hola, muchas gracias. Me puedes ayudar para lograr que en lugar de ponerla en contenedor y descargarla, se pueda agregar un botón para enviar por whatsapp? Gracias.
Por supuesto, estaré encantado de ayudarle más a fondo. Ofrezco servicios de consultoría personalizados para resolver problemas específicos. Si está interesado, envíeme un mensaje a https://parzibyte.me/#contacto y podemos conversar sobre cómo puedo ayudarle.
Muchas gracias, me ha sido de gran utilidad. Saludos
hace poco que estoy aprendiendo programación web
acabo de usar lo del ejemplo 4 en un pequeño proyecto
gracias
Estoy intentando hacer funcionar html2canvas para guardar un div en una imagen y no funciona, lo probe en diferentes navegadores y no hace nada.
No me arroja ningun error tampoco al presionar el boton de tomar captura. no me hace nada.
Lo unico que se me ocurre es que le falte alguna libreria js.
Podrian ayudarme facilitandomelas o de donde las estan usando?
lindo post, el tema es que me esta poniendo muy nervioso el tema del cors, ya que el todataurl no funciona con referencias cruzadas , tengo entendido que se pueden usr proxys pero la verdad que no pude lograrlo
Buen post, muy completo.
Gracias por sus comentarios. No olvide seguirme y compartir
Lo he probado y funciona genial, muchísimas gracias, me fue de gran ayuda!
Me da gusto que haya funcionado. Te invito a seguirme en mis redes sociales para estar al tanto de mi contenido.
Saludos :)
Okey, muchas gracias, le echo un ojo a ver que tal.
Hola, muchísimas gracias por le tutorial, me valió bastante como ayuda.
Tengo una pregunta, el .png generado, ¿podemos usarlo para guardarla en una ruta especifica del servidor? De ser así, ¿Cómo sería?
Muchas gracias de antemano.
Gracias por tus comentarios. Para guardarla en el servidor mira este tutorial con PHP: https://parzibyte.me/blog/2019/07/12/tomar-screenshot-pagina-web-enviarla-a-php-html2canvas/
Incluso si no usas PHP en el backend, te puede funcionar para entenderlo, ya que cuando se descarga se hace del lado del cliente y no puedes guardarla en una ruta específica, pero cuando lo envías al servidor sí.
Te invito a seguirme en mis redes sociales. Saludos