Procesamiento de imágenes con JavaScript
El procesamiento del lado del cliente es un aspecto fundamental en la creación de sitios web modernos y aplicaciones web interactivas. Por ello es más que sabido que todo lo que podamos calcular o procesar primero en el navegador, redunda en el rendimiento y presentación general del servicio que prestamos con nuestros proyectos de software.
En alguna ocasión puede ser útil permitir al usuario que tome algún archivo de imagen mediante nuestra aplicación web, sin necesidad de enviarla al servidor (o retrasar ese envío para algún momento más adelante) y trabajar con ella primero del lado del cliente. Esto puede estar motivado por varias razones, entre otras:
- Decisiones de performance, como por ejemplo solo necesito un thumbnail de la imagen antes de hacer submit o éste se hará después (o nunca).
- Reducir el tamaño o calidad de la imagen en cuestión antes de enviarla por la red, por ahorro de tráfico o por la naturaleza o forma de trabajo de mi aplicación.
- Queremos mejorar la experiencia de usuario y pretendemos utilizar técnicas de reducción de tamaño o compresión para optimizar el rendimiento.
- Cualquier otro preprocesamiento necesario.
En resumen, permitir que los usuarios carguen y puedan manipular archivos de imagen directamente en el navegador es una forma efectiva de mejorar el rendimiento y la calidad de nuestras aplicaciones en línea, al aprovechar al máximo las capacidades del navegador y utilizar técnicas de procesamiento de imágenes eficaces.
Cuando hablamos de procesamiento del lado del cliente, por supuesto que la aproximación directa es usar JavaScript para llevar a cabo esta tarea. Dada su capacidad para actuar con el DOM y realizar operaciones en tiempo real, es la opción preferida para hacerlo.
Con este contexto en mente, presento este ejemplo, escrito puramente en JavaScript y embebido en una hoja HTML. Pero no está demás decir que es código que puede fácilmente usarse dentro de algún frontend actual o mejorarse con las facilidades que presta alguna librería como jQuery. De hecho, este método fue usado en un caso de uso real, dividido en un par de pasos o submétodos en un script de Vue.js.
A veces, es posible que necesitemos redimensionar o adaptar imágenes específicas para ajustarlas a un tamaño o formato predeterminado.
Llevaré esto a un ejemplo práctico: Tenemos la situación en la que queremos trabajar con imágenes de sólo 500x500 px, por lo que necesitaremos de un canvas cuadrado de ese tamaño para que las imágenes seleccionadas se adapten correctamente a él. Al utilizar este enfoque, podemos garantizar que todas las imágenes tengan un tamaño uniforme, conocido con antelación y adaptado perfectamente a nuestras condiciones de trabajo, de ancho de banda o al diseño que tan minuciosamente implementamos en nuestro frontend.
En pocas palabras, la secuencia y su explicación son como sigue:
Tenemos un archivo HTML de lo más simple, dentro del DOM un botón que llama a la función que hará la tarea y un div al que finalmente adjuntaremos el canvas resultante.
Al hacer clic en el botón, se llama y corre la función getFile, que será asíncrona.
Ahora, dentro de la función establecemos el objeto archivo, con el cual permitimos al usuario elegir un archivo de imagen utilizando el selector del objeto window. Este selector abre lo que conocemos como el cuadro de diálogo de selección de archivos de nuestro sistema operativo, como si se tratara de un botón input de tipo file.
Definimos una variable con la foto en cuestión, la única dentro de la lista de archivos elegidos (que pueden ser más, dependiendo de si hemos seteado la selección múltiple de archivos o no). Estas operaciones deben esperar su concreción para continuar y por eso las llamamos con un await.
Entonces, de ser necesario, puede definirse la opción multiple para permitir la selección de varios archivos a la vez, lo que requerirá cambios en el código, como por ejemplo una recorrida por todo el array que constituye la lista de archivos.
En este momento definiremos un reader para nuestra foto, que tiene un evento onerror que podemos usar en caso de que algo no salga como esperamos.
Dentro del reader tomamos los datos de la foto y establecemos el tamaño máximo para nuestro canvas.
También creamos un elemento imagen que no mostraremos en pantalla; simplemente lo usaremos para trabajar con la imagen seleccionada e incorporarla más tarde al canvas definido con anterioridad.
El objeto Image también tiene un evento onerror que podemos capturar.
Dentro del evento onload de la imagen, vamos a construir el canvas.
Recordemos que canvas es un elemento tipo lienzo en blanco para nuestro DOM y que podemos usar en nuestros sitios, ya que es un elemento HTML. Permite dibujar gráficos a través de secuencias de comandos (por lo general, JavaScript), para hacer composiciones de fotos o incluso animaciones.
La mejor decisión sería calcular el tamaño final de la imagen y colocarla proporcionalmente dentro del lienzo. Este caso considera imágenes horizontales de mayor tamaño que el canvas, pero puede adaptarse de forma muy sencilla. Usamos regla de tres simple, para calcular la tasa de reducción del size (aquí, para la altura de la imagen) y luego un cálculo de su posición final de acuerdo con el tamaño del lienzo.
Luego, dibujamos la imagen en el canvas y adjuntamos el canvas al div de nuestro DOM para así renderizar en pantalla mediante append.
Ahora sí, estamos listos y puede que queramos enviar el canvas resultante al servidor (o al menos tenerlo disponible para ello), por lo que debemos pasarlo a imagen. Para ello, convertiremos el canvas a un blob, es decir, un objeto binario grande.
Luego, el blob del canvas puede enviarse a un endpoint preciso de backend que reciba esta nueva imagen (con su nueva forma y tamaño) y la convierta en archivo apropiadamente o realice algún otro procesamiento (como por ejemplo análisis o reconocimientos mediante IA).
Aquí adjunto un captura del resultado y el código completo, con algunos comentarios:
¡Listo! Ahora ya sabes cómo procesar imágenes con JavaScript de manera sencilla y efectiva.
Todo el éxito en tus proyectos futuros.
¡Saludos!
Listopro Community da la bienvenida a todas las razas, etnias, nacionalidades, credos, géneros, orientaciones, puntos de vista e ideologías, siempre y cuando promuevan la diversidad, la equidad, la inclusión y el crecimiento profesional de los profesionales en tecnología.