Captura de pantalla con JavaScript y HTML2Canvas

Descubre cómo añadir a tu aplicación web, la funcionalidad de realizar una captura de pantalla con JavaScript y la librería HTML2Canvas.

https://html2canvas.hertzen.com/ Screenshots with JavaScript

Todos mis artículos van acompañados de un ejercicio práctico. Haz click en la siguiente imágen para ver el resultado final de la práctica de hoy:

Captura de pantalla con JavaScript y HTML2Canvas
Captura de pantalla con JavaScript y HTML2Canvas

Si quieres ir directamente a la parte práctica, haz click aquí

Hace unos días estaba utilizando un software de gestión de contenidos, cuando de repente, detecté un problema en un conjunto de tablas. 

Esta situación no habría tenido mayor trascendencia, de no ser por un detalle que me llamó poderosamente la atención.

El caso es que, en el momento de reportar dicho problema, el programa me ofreció la posibilidad de adjuntar una captura de pantalla de la interfaz de usuario.

Quizá no te parezca gran cosa, e incluso pienses que me sorprendo con muy poco. Puede que tengas razón, pero lo cierto es que, irremediablemente, mi faceta de desarrollador, se puso a pensar cómo se implementaría una solución así.

En primer lugar, hay que tener en cuenta que, una página web se construye mediante un etiquetado de texto HTML.

Posteriormente, ese documento se «parsea» para construir el árbol DOM, y así, poder manipularlo mediante JavaScript.

Este proceso da como resultado una estructura de componentes web dinámicos, los cuales, pueden mostrar información muy dispar.

Para que te hagas una idea, en una misma sección de una página web, puede aparecer un apartado con texto enriquecido, un carrousel de imágenes, botones de todo tipo, e incluso vídeos.

Ese dinamismo es lo que hace que extraer una captura de pantalla de todo, pueda resultar complejo.

Wireframes web y screenshot
Wireframes web y screenshot

Pero por suerte, la comunidad de desarrolladores JavaScript es enorme. Siempre hay alguien que ha resuelto la necesidad, y la ha puesto a disposición de todo el mundo.

HTML2Canvas, la librería que convierte código HTML a imágenes

En esta ocasión te voy a hablar de HTML2Canvas

Una librería JavaScript desarrollada por Niklas von Hertzen, capaz de plasmar el contenido HTML en una imagen dentro de un «canvas».

Su repositorio de Github tiene más de 26k estrellas, y se encuentra mantenido por un equipo de 50 personas. Además, el package NPM se descarga semanalmente más de 1M de veces.

Su implementación es extremadamente sencilla, basta con ejecutar el siguiente comando para disponer de la herramienta en tu proyecto frontend.

npm i html2canvas

Tras instalar la librería, se puede importar con la instrucción

import html2canvas from 'html2canvas';

El módulo ‘html2canvas’ expone una función con el mismo nombre, y esta admite dos parámetros.

El primero se trata del elemento del DOM que deseamos convertir en imagen. Por consiguiente, si pasamos el objeto «body» obtendremos una captura del total de la página.

Esa opción es genial, ya que, si por el contrario solo se necesita extraer una imágen de una parte de la aplicación, solo hace falta seleccionar el contenedor adecuado. 

Al final de este escrito implementaremos un ejercicio práctico donde veremos ese comportamiento en marcha. 

Por otra parte, el segundo parámetro de la función admite un objeto JavaScript de configuración. En él se pueden definir y ajustar determinados comportamientos mediante una serie de claves y valores.

A continuación te listo algunos de los más relevantes.

  • backgroundColor: Se trata de una propiedad para declarar un color de fondo en caso de que el elemento no lo tenga definido. 
  • canvas: Útil para aprovechar una  etiqueta canvas propia.
  • logging: Este parámetro proveerá de información para el desarrollo.
  • width y heigth: por defecto, HTML2Canvas establecerá el tamaño de la imagen a partir del contenedor seleccionado, pero con estos atributos se pueden sobrescribir. 

Si quieres ver en detalle todas las opciones de configuración, puedes consultar el siguiente enlace:

https://html2canvas.hertzen.com/configuration

Ya puestos, puedes dedicarle unos minutos a ver el resto de la documentación, si lo crees necesario.

Obtener pantallazos de la interfaz gráfica

Con este breve vistazo a su API, ya podemos poner en práctica la librería a través de un sencillo ejercicio.

Para que te resulte más cómodo seguir el tutorial, te dejo un enlace al código completo, a través del repositorio de libreriasjs en Github

https://github.com/Danivalldo/libreriasjs/tree/master/HTML2Canvas

Simulando un poco el caso que te he presentado al inicio del artículo, prepararemos una vista «dashboard» de una aplicación ficticia

Posteriormente, implementaremos en ella la opción de realizar una captura de pantalla total o parcial. Adicionalmente, añadiremos la posibilidad de descargar la imagen obtenida.

Comenzamos creando la estructura HTML básica

En ella definiremos los espacios para montar un “sidebar” y una área de contenido. Además, incluiremos un contenedor que actuará de “modal”.

<body>
  <div class="app">
    <div class="sidebar-container">
      <ul class="unstyled">
        <li>
          <a href="#"><img src="icons/home.svg" alt="" /></a>
        </li>
        <li>
          <a href="#"><img src="icons/dashboard.svg" alt="" /></a>
        </li>
        <li>
          <a href="#"><img src="icons/user.svg" alt="" /></a>
        </li>
        <li>
          <a href="#"><img src="icons/document.svg" alt="" /></a>
        </li>
      </ul>
    </div>
    <div class="main-container">
      <div class="header-container">
        <h1>Dashboard</h1>
        <div class="user-badge">
          <div class="profile-img">
            <img src="images/user.jpg" alt="" />
          </div>
          <span>Jane Doe</span>
        </div>
      </div>
      <div id="content" class="content-container">
        <div class="row">
          <div class="card bg-gradient take-screenshot-card">
            <h2>Captura de pantalla</h2>
            <div class="form-wrapper">
              <select name="" class="select-area">
                <option value="" disabled>Selecciona una seccion</option>
                <option value="fullscreen" selected>Pantalla completa</option>
                <option value="content">Contenido</option>
                <option value="table">Tabla</option>
                <option value="chart">Gráfico</option>
              </select>
              <button class="btn with-icon tertiary capture-btn">
                <img src="icons/camera.svg" alt="" />
                <span>Realizar captura de pantalla</span>
              </button>
            </div>
          </div>
      </div>
      <div class="footer-container">Fake Dashboard 2022 | libreriasJs</div>
    </div>
  </div>
  <div class="modal">
    <div class="content-modal card">
      <h2>Captura de pantalla</h2>
      <div>
        <button class="btn save-snapshot-btn">Guardar</button>
        <button class="btn close-modal-btn">Cerrar</button>
      </div>
      <hr />
      <div class="placeholder-canvas"></div>
    </div>
  </div>
</body>

De momento solo he añadido parte del contenido HTML. Incluir todas las etiquetas, alargaría innecesariamente este escrito.

Puedes copiar el código de cada contenedor visitando directamente el repositorio de Github. O bien, puedes inventarte nuevo contenido para experimentar tú mismo con distintos resultados.

Te animo a que pruebes con distintos elementos, tales como listas, gráficos SVG, tablas, botones, etc.

En esta ocasión, no he cargado ninguna librería CSS externa para dar estilo al proyecto. Lo que he hecho, ha sido prepararmúltiples clases para cada componente web, utilizando el preprocesador SASS.

Te dejo enlaces directos a los estilos de cada componente:

El archivo ./src/SASS/index.sass se encarga de importar cada uno de estos elementos, y dar estilos básicos a la estructura principal.

@import url('https://fonts.googleapis.com/css2?family=Nunito:wght@300;500&display=swap')
@import './header'
@import './sidebar'
@import './footer'
@import './card'
@import './button'
@import './lists'
@import './table'
@import './modal'
*
  box-sizing: border-box
:root
  --main: rgb(52,77,200)
  --secondary: rgb(29,46,124)
  --tertiary: rgb(90,110, 209)
  --complementary: rgb(244,247,250)
.text-main
  color: var(--main)
.text-secondary
  color: var(--secondary)
.text-center
  text-align: center
body
  margin: 0
  padding: 0
  background-color: var(--complementary)
  font-family: 'Nunito', sans-serif
.app
  box-sizing: border-box
  display: flex
  min-height: 100vh
  flex-direction: row
  .main-container
    flex: 1
    overflow-x: auto
.content-container
  box-sizing: border-box
  padding: 10px
  .row
    display: flex
    flex-direction: row
    & > div
      flex: 1
@media screen and (max-width: 910px)
  .content-container
    .row
      display: block

Podría desarrollar un poco más ésta parte del tutorial, pero de nuevo, considero que es innecesario, y entorpecería el auténtico punto a cubrir.

Ese punto, por supuesto, es la implementación de la librería, y a continuación voy a detallar cómo hacerlo.

Para empezar, instalaremos la librería HTML2Canvas mediante el comando que sigue:

npm i html2canvas

Una vez instalado, lo importamos en nuestro archivo src/index.js. Por supuesto, también importamos los estilos antes definidos.

import html2canvas from "html2canvas";
import "./SASS/index.sass";

Seguidamente guardamos un conjunto de referencias a distintos elementos del DOM. Ya que luego las utilizaremos para capturar eventos de click, o actualizar la interfaz.

const captureBtn = document.querySelector(".capture-btn");
const modal = document.querySelector(".modal");
const canvasContainer = modal.querySelector(".placeholder-canvas");
const selectArea = document.querySelector(".select-area");

Por ejemplo, una modificación en la UI será mostrar y ocultar una modal. Para ello, habilitamos la función “toggleModal

const toggleModal = () => {
  if (modal.classList.contains("active")) {
    canvasContainer.innerHTML = "";
    return modal.classList.remove("active");
  }
  modal.classList.add("active");
};

Esta función tan solo se encargará de añadir o quitar la clase CSS “active”, y vaciar el contenido de la modal en el momento de ocultarla.

Naturalmente, también vamos a preparar una función específica para realizar la captura de pantalla.

Llamaremos a esa función “takeSnapShot”.

const takeSnapShot = async () => {
  const area =
    selectArea.value === "fullscreen"
      ? document.body
      : document.querySelector(`#${selectArea.value}`);
  const canvas = await html2canvas(area, {
    allowTaint: true,
  });
  canvasContainer.appendChild(canvas);
};

Con las primeras instrucciones, extraemos qué área ha seleccionado el usuario para extraer una imágen.

Guardamos en una variable “area” una referencia al elemento del DOM correspondiente. Siendo “fullscreen” una referencia al cuerpo de la página web.

Acto seguido, llamamos a la función “html2canvas”, pasando como primer parámetro la variable area. Tal y como ya vimos, el segundo parámetro es opcional. En este caso lo incluiremos para especificar que se puedan obtener imágenes de orígenes distintos a nuestro servidor.

Cerramos el cuerpo de la función, incluyendo dentro de la modal, la etiqueta canvas obtenida.

Tan solo queda capturar los eventos de tipo click de los botones correspondientes, y asignar funciones de “callback”.

captureBtn.addEventListener("click", async () => {
  await takeSnapShot();
  toggleModal();
});

El botón “captureBtn” se encargará de llamar a la función “takeSnapShot”, y una vez completada la captura de pantalla, mostrará la modal mediante “toggleModal”.

modal.addEventListener("click", async (e) => {
  if (e.target.classList.contains("close-modal-btn")) {
    return toggleModal();
  }
  const container = e.target.closest(".card");
  if (!container) {
    return toggleModal();
  }
});

Por otro lado, también escucharemos los clicks realizados dentro la modal. Si uno de estos clicks se realiza sobre el botón con la clase “close-modal-btn”, o bien, sobre un elemento que no desciende del elemento “card”, cerraremos la modal llamando de nuevo a “toggleModal”.

Amplía el ejercicio guardando la imágen en local

Llegados a este punto, podríamos dar por cerrada esta pequeña guía. Sin embargo, podría ser interesante incluir la funcionalidad de guardar la imagen en nuestro dispositivo.

Hace unas semanas vimos una librería específicamente diseñada para este propósito.

Descargar archivos con JavaScript y FileSaverJs

Te animo a que leas el artículo, y trates de implementar la funcionalidad tu mismo. Por supuesto, en el repositorio de Github encontrarás el código implementado.

Espero que este artículo te haya resultado interesante. Por mi parte, he descubierto una nueva librería, que estoy seguro que tarde o temprano me resultará útil como desarrollador.

Te dejo algunos recursos mas, por si te apetece ampliar lo aprendido hoy:

Por cierto, si no quieres perderte ninguna publicación nueva, puedes seguirme en mi canal de Instagram, te prometo que solo publico allí, si hay alguna novedad en el blog.

Un abrazo desarrollador.

Deja un comentario